diff --git a/CHANGELOG b/CHANGELOG
index 031536816a5e17beea7afa2ee57c01c04a328fc4..7d61fef39a4525a7b7a92e2a38031ebb125ce25c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -24,6 +24,7 @@ v 6.7.0
   - Faster authorized_keys rebuilding in `rake gitlab:shell:setup` (requires gitlab-shell 1.8.5)
   - Create and Update MR calls now support the description parameter (Greg Messner)
   - Markdown relative links in the wiki link to wiki pages, markdown relative links in repositories link to files in the repository
+  - Added Slack service integration (Federico Ravasio)
 
 v 6.6.5
   - Added option to remove issue assignee on project issue page and issue edit page (Jason Blanchard)
diff --git a/Gemfile b/Gemfile
index e3acf4a153d72c9d993c6a9dd9e867e4e7d0f3e4..6a587d1d279e6ed33cfd6e026a27a21736ad8c99 100644
--- a/Gemfile
+++ b/Gemfile
@@ -132,6 +132,9 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2"
 # Gemnasium integration
 gem "gemnasium-gitlab-service", "~> 0.2"
 
+# Slack integration
+gem "slack-notifier", "~> 0.2.0"
+
 # d3
 gem "d3_rails", "~> 3.1.4"
 
diff --git a/Gemfile.lock b/Gemfile.lock
index 52d6ac314633454bca33d6f7b146ac43c6fdc40b..de1ef59712ec067761b05f9b7049bf4658a3af5f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -468,6 +468,7 @@ GEM
       rack-protection (~> 1.4)
       tilt (~> 1.3, >= 1.3.4)
     six (0.2.0)
+    slack-notifier (0.2.0)
     slim (2.0.2)
       temple (~> 0.6.6)
       tilt (>= 1.3.3, < 2.1)
@@ -652,6 +653,7 @@ DEPENDENCIES
   simplecov
   sinatra
   six
+  slack-notifier (~> 0.2.0)
   slim
   spinach-rails
   spork (~> 1.0rc)
diff --git a/app/models/project.rb b/app/models/project.rb
index 7d7edc457391a94c34ea61f8f77f8b9d88e0eb18..769ab217625cb104545ce97bd17aee59dd0262bc 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -56,6 +56,7 @@ class Project < ActiveRecord::Base
   has_one :flowdock_service, dependent: :destroy
   has_one :assembla_service, dependent: :destroy
   has_one :gemnasium_service, dependent: :destroy
+  has_one :slack_service, dependent: :destroy
   has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
   has_one :forked_from_project, through: :forked_project_link
   # Merge Requests for target project should be removed with it
@@ -304,7 +305,7 @@ def build_missing_services
   end
 
   def available_services_names
-    %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium)
+    %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack)
   end
 
   def gitlab_ci?
diff --git a/app/models/project_services/slack_message.rb b/app/models/project_services/slack_message.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b2b8d6fed7a161151ec250e769cb40be6a82aacc
--- /dev/null
+++ b/app/models/project_services/slack_message.rb
@@ -0,0 +1,95 @@
+require 'slack-notifier'
+
+class SlackMessage
+  def initialize(params)
+    @after = params.fetch(:after)
+    @before = params.fetch(:before)
+    @commits = params.fetch(:commits, [])
+    @project_name = params.fetch(:project_name)
+    @project_url = params.fetch(:project_url)
+    @ref = params.fetch(:ref).gsub('refs/heads/', '')
+    @username = params.fetch(:user_name)
+  end
+
+  def compose
+    format(message)
+  end
+
+  private
+
+  attr_reader :after
+  attr_reader :before
+  attr_reader :commits
+  attr_reader :project_name
+  attr_reader :project_url
+  attr_reader :ref
+  attr_reader :username
+
+  def message
+    if new_branch?
+      new_branch_message
+    elsif removed_branch?
+      removed_branch_message
+    else
+      push_message << commit_messages
+    end
+  end
+
+  def format(string)
+    Slack::Notifier::LinkFormatter.format(string)
+  end
+
+  def new_branch_message
+    "#{username} pushed new branch #{branch_link} to #{project_link}"
+  end
+
+  def removed_branch_message
+    "#{username} removed branch #{ref} from #{project_link}"
+  end
+
+  def push_message
+    "#{username} pushed to branch #{branch_link} of #{project_link} (#{compare_link})"
+  end
+
+  def commit_messages
+    commits.each_with_object('') do |commit, str|
+      str << compose_commit_message(commit)
+    end
+  end
+
+  def compose_commit_message(commit)
+    id = commit.fetch(:id)[0..5]
+    message = commit.fetch(:message)
+    url = commit.fetch(:url)
+
+    "\n - #{message} ([#{id}](#{url}))"
+  end
+
+  def new_branch?
+    before =~ /000000/
+  end
+
+  def removed_branch?
+    after =~ /000000/
+  end
+
+  def branch_url
+    "#{project_url}/commits/#{ref}"
+  end
+
+  def compare_url
+    "#{project_url}/compare/#{before}...#{after}"
+  end
+
+  def branch_link
+    "[#{ref}](#{branch_url})"
+  end
+
+  def project_link
+    "[#{project_name}](#{project_url})"
+  end
+
+  def compare_link
+    "[Compare changes](#{compare_url})"
+  end
+end
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..27648acf6d0c406ddb4d5e65d39a7f86fe848935
--- /dev/null
+++ b/app/models/project_services/slack_service.rb
@@ -0,0 +1,67 @@
+# == Schema Information
+#
+# Table name: services
+#
+#  id          :integer          not null, primary key
+#  type        :string(255)
+#  title       :string(255)
+#  token       :string(255)
+#  project_id  :integer          not null
+#  created_at  :datetime         not null
+#  updated_at  :datetime         not null
+#  active      :boolean          default(FALSE), not null
+#  project_url :string(255)
+#  subdomain   :string(255)
+#  room        :string(255)
+#  api_key     :string(255)
+#
+
+class SlackService < Service
+  attr_accessible :room
+  attr_accessible :subdomain
+
+  validates :room, presence: true, if: :activated?
+  validates :subdomain, presence: true, if: :activated?
+  validates :token, presence: true, if: :activated?
+
+  def title
+    'Slack'
+  end
+
+  def description
+    'A team communication tool for the 21st century'
+  end
+
+  def to_param
+    'slack'
+  end
+
+  def fields
+    [
+      { type: 'text', name: 'subdomain', placeholder: '' },
+      { type: 'text', name: 'token',     placeholder: '' },
+      { type: 'text', name: 'room',      placeholder: '' },
+    ]
+  end
+
+  def execute(push_data)
+    message = SlackMessage.new(push_data.merge(
+      project_url: project_url,
+      project_name: project_name
+    ))
+
+    notifier = Slack::Notifier.new(subdomain, token)
+    notifier.channel = room
+    notifier.ping(message.compose)
+  end
+
+  private
+
+  def project_name
+    project.name_with_namespace.gsub(/\s/, '')
+  end
+
+  def project_url
+    project.web_url
+  end
+end
diff --git a/features/project/service.feature b/features/project/service.feature
index 46b983e8f9a36d0312d47357b892dafd3dec915b..a5af065c9e7da2438d0f244208ca7418d516dde9 100644
--- a/features/project/service.feature
+++ b/features/project/service.feature
@@ -37,6 +37,12 @@ Feature: Project Services
     And I fill Assembla settings
     Then I should see Assembla service settings saved
 
+  Scenario: Activate Slack service
+    When I visit project "Shop" services page
+    And I click Slack service link
+    And I fill Slack settings
+    Then I should see Slack service settings saved
+
   Scenario: Activate email on push service
     When I visit project "Shop" services page
     And I click email on push service link
diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb
index 54b3f18e0845a2f9503cb0f788c86fca9e1d60a2..0594a08a5e735c87009dcac9b03d8bd7a8516cf8 100644
--- a/features/steps/project/services.rb
+++ b/features/steps/project/services.rb
@@ -100,4 +100,22 @@ class ProjectServices < Spinach::FeatureSteps
   step 'I should see email on push service settings saved' do
     find_field('Recipients').value.should == 'qa@company.name'
   end
+
+  step 'I click Slack service link' do
+    click_link 'Slack'
+  end
+
+  step 'I fill Slack settings' do
+    check 'Active'
+    fill_in 'Subdomain', with: 'gitlab'
+    fill_in 'Room', with: '#gitlab'
+    fill_in 'Token', with: 'verySecret'
+    click_button 'Save'
+  end
+
+  step 'I should see Slack service settings saved' do
+    find_field('Subdomain').value.should == 'gitlab'
+    find_field('Room').value.should == '#gitlab'
+    find_field('Token').value.should == 'verySecret'
+  end
 end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 6bae5951b7b2113ddcdd6ef5591ed50fe22a8ada..839350bafbfa2208360c8285f32c7f475359dc5d 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -47,6 +47,7 @@
     it { should have_many(:hooks).dependent(:destroy) }
     it { should have_many(:protected_branches).dependent(:destroy) }
     it { should have_one(:forked_project_link).dependent(:destroy) }
+    it { should have_one(:slack_service).dependent(:destroy) }
   end
 
   describe "Mass assignment" do
diff --git a/spec/models/slack_message_spec.rb b/spec/models/slack_message_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b39cd4edf820279d3c2fac76e775cb33ea044b62
--- /dev/null
+++ b/spec/models/slack_message_spec.rb
@@ -0,0 +1,56 @@
+require_relative '../../app/models/project_services/slack_message'
+
+describe SlackMessage do
+  subject { SlackMessage.new(args) }
+
+  let(:args) {
+    {
+      after: 'after',
+      before: 'before',
+      project_name: 'project_name',
+      ref: 'refs/heads/master',
+      user_name: 'user_name',
+      project_url: 'url'
+    }
+  }
+
+  context 'push' do
+    before do
+      args[:commits] = [
+        { message: 'message1', url: 'url1', id: 'abcdefghi' },
+        { message: 'message2', url: 'url2', id: '123456789' },
+      ]
+    end
+
+    it 'returns a message regarding pushes' do
+      subject.compose.should ==
+        'user_name pushed to branch <url/commits/master|master> of ' <<
+        '<url|project_name> (<url/compare/before...after|Compare changes>)' <<
+        "\n - message1 (<url1|abcdef>)" <<
+        "\n - message2 (<url2|123456>)"
+    end
+  end
+
+  context 'new branch' do
+    before do
+      args[:before] = '000000'
+    end
+
+    it 'returns a message regarding a new branch' do
+      subject.compose.should ==
+        'user_name pushed new branch <url/commits/master|master> to ' <<
+        '<url|project_name>'
+    end
+  end
+
+  context 'removed branch' do
+    before do
+      args[:after] = '000000'
+    end
+
+    it 'returns a message regarding a removed branch' do
+      subject.compose.should ==
+        'user_name removed branch master from <url|project_name>'
+    end
+  end
+end
diff --git a/spec/models/slack_service_spec.rb b/spec/models/slack_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..387455cb25e01825012187c577fd372328515b7f
--- /dev/null
+++ b/spec/models/slack_service_spec.rb
@@ -0,0 +1,69 @@
+# == Schema Information
+#
+# Table name: services
+#
+#  id          :integer          not null, primary key
+#  type        :string(255)
+#  title       :string(255)
+#  token       :string(255)
+#  project_id  :integer          not null
+#  created_at  :datetime         not null
+#  updated_at  :datetime         not null
+#  active      :boolean          default(FALSE), not null
+#  project_url :string(255)
+#  subdomain   :string(255)
+#  room        :string(255)
+#  api_key     :string(255)
+#
+
+require 'spec_helper'
+
+describe SlackService do
+  describe "Associations" do
+    it { should belong_to :project }
+    it { should have_one :service_hook }
+  end
+
+  describe "Validations" do
+    context "active" do
+      before do
+        subject.active = true
+      end
+
+      it { should validate_presence_of :room }
+      it { should validate_presence_of :subdomain }
+      it { should validate_presence_of :token }
+    end
+  end
+
+  describe "Execute" do
+    let(:slack) { SlackService.new }
+    let(:user) { create(:user) }
+    let(:project) { create(:project) }
+    let(:sample_data) { GitPushService.new.sample_data(project, user) }
+    let(:subdomain) { 'gitlab' }
+    let(:token) { 'verySecret' }
+    let(:api_url) {
+      "https://#{subdomain}.slack.com/services/hooks/incoming-webhook?token=#{token}"
+    }
+
+    before do
+      slack.stub(
+        project: project,
+        project_id: project.id,
+        room: '#gitlab',
+        service_hook: true,
+        subdomain: subdomain,
+        token: token
+      )
+
+      WebMock.stub_request(:post, api_url)
+    end
+
+    it "should call Slack API" do
+      slack.execute(sample_data)
+
+      WebMock.should have_requested(:post, api_url).once
+    end
+  end
+end