diff --git a/CHANGELOG b/CHANGELOG
index d53429a5d44c9198c446edf2e352d1c97cd9b0b1..77348e3d38285bb372bda48e2e0d73534284fba6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,24 +1,33 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
 v 7.11.0 (unreleased)
+  - Get Gitorious importer to work again.
   - Fix clone URL field and X11 Primary selection (Dmitry Medvinsky)
   - Ignore invalid lines in .gitmodules
   - Fix "Cannot move project" error message from popping up after a successful transfer (Stan Hu)
   - Redirect to sign in page after signing out.
+  - Fix "Hello @username." references not working by no longer allowing usernames to end in period.
   -
   - Add "Reply quoting selected text" shortcut key (`r`)
   - Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention.
   - Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention.
+  - Added GitLab Event header for project hooks
   -
+  - Show Atom feed buttons everywhere where applicable.
+  - Add project activity atom feed.
+  - Don't crash when an MR from a fork has a cross-reference comment from the target project on of its commits.
+  - Include commit comments in MR from a forked project.
   -
-  -
-  -
+  - Add default project and snippet visibility settings to the admin web UI.
+  - Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu)
   - Move snippets UI to fluid layout
   - Improve UI for sidebar. Increase separation between navigation and content
   - Improve new project command options (Ben Bodenmiller)
   - Prevent sending empty messages to HipChat (Chulki Lee)
+  - Improve UI for mobile phones on dashboard and project pages
+  - Add room notification and message color option for HipChat
 
-v 7.10.0 (unreleased)
+v 7.10.0
   - Ignore submodules that are defined in .gitmodules but are checked in as directories.
   - Allow projects to be imported from Google Code.
   - Remove access control for uploaded images to fix broken images in emails (Hannes Rosenögger)
diff --git a/Gemfile b/Gemfile
index 238337d7731ba0d71264eae527835e91b7ae1bf6..27abaf3f5d3ee39fed49a77fe30d93152a9c9d67 100644
--- a/Gemfile
+++ b/Gemfile
@@ -159,7 +159,7 @@ gem "slack-notifier", "~> 1.0.0"
 gem 'asana', '~> 0.0.6'
 
 # d3
-gem "d3_rails", "~> 3.1.4"
+gem 'd3_rails', '~> 3.5.5'
 
 #cal-heatmap
 gem "cal-heatmap-rails", "~> 0.0.1"
@@ -189,7 +189,7 @@ gem 'turbolinks'
 gem 'jquery-turbolinks'
 
 gem 'select2-rails'
-gem 'jquery-atwho-rails', "~> 0.3.3"
+gem 'jquery-atwho-rails', '~> 1.0.0'
 gem "jquery-rails"
 gem "jquery-ui-rails"
 gem "jquery-scrollto-rails"
@@ -224,14 +224,13 @@ end
 group :development, :test do
   gem 'coveralls', require: false
   gem 'rubocop', '0.28.0', require: false
-  # gem 'rails-dev-tweaks'
   gem 'spinach-rails'
   gem "rspec-rails", '2.99'
-  gem "capybara", '~> 2.2.1'
+  gem 'capybara', '~> 2.2.1'
+  gem 'capybara-screenshot', '~> 1.0.0'
   gem "pry-rails"
   gem "awesome_print"
   gem "database_cleaner"
-  gem "launchy"
   gem 'factory_girl_rails'
 
   # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
diff --git a/Gemfile.lock b/Gemfile.lock
index 9d17cd1128270ef62f16f9336975ada0f8ecce57..79797f98030c7e5af32ee974aa7d7b47a72b3827 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -85,6 +85,9 @@ GEM
       rack (>= 1.0.0)
       rack-test (>= 0.5.4)
       xpath (~> 2.0)
+    capybara-screenshot (1.0.9)
+      capybara (>= 1.0, < 3)
+      launchy
     carrierwave (0.9.0)
       activemodel (>= 3.2.0)
       activesupport (>= 3.2.0)
@@ -116,7 +119,7 @@ GEM
     crack (0.4.1)
       safe_yaml (~> 0.9.0)
     creole (0.3.8)
-    d3_rails (3.1.10)
+    d3_rails (3.5.5)
       railties (>= 3.1.0)
     daemons (1.1.9)
     database_cleaner (1.3.0)
@@ -298,7 +301,7 @@ GEM
       phantomjs (>= 1.9)
       railties (>= 3.2.0)
       sprockets-rails
-    jquery-atwho-rails (0.3.3)
+    jquery-atwho-rails (1.0.1)
     jquery-rails (3.1.0)
       railties (>= 3.0, < 5.0)
       thor (>= 0.14, < 2.0)
@@ -677,13 +680,14 @@ DEPENDENCIES
   byebug
   cal-heatmap-rails (~> 0.0.1)
   capybara (~> 2.2.1)
+  capybara-screenshot (~> 1.0.0)
   carrierwave
   charlock_holmes
   coffee-rails
   colored
   coveralls
   creole (~> 0.3.6)
-  d3_rails (~> 3.1.4)
+  d3_rails (~> 3.5.5)
   database_cleaner
   default_value_for (~> 3.0.0)
   devise (= 3.2.4)
@@ -720,13 +724,12 @@ DEPENDENCIES
   httparty
   jasmine (~> 2.2.0)
   jasmine-rails
-  jquery-atwho-rails (~> 0.3.3)
+  jquery-atwho-rails (~> 1.0.0)
   jquery-rails
   jquery-scrollto-rails
   jquery-turbolinks
   jquery-ui-rails
   kaminari (~> 0.15.1)
-  launchy
   letter_opener
   minitest (~> 5.3.0)
   mousetrap-rails
diff --git a/README.md b/README.md
index 852d9443609b14324fbd9601cd1498519f5b99cd..f3330dfe86a4d9939d8eaf9c157ac0b9a82dcd1d 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,9 @@
+## Canonical source
+
+The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible.
+
+The source of GitLab Enterprise Edition is [hosted on GitLab.com](https://gitlab.com/subscribers/gitlab-ee) and acessible only to [subscribers](https://about.gitlab.com/subscription/).
+
 # ![logo](https://about.gitlab.com/images/gitlab_logo.png) GitLab
 
 ## Subscriber onboarding information
@@ -43,10 +49,6 @@ There are two editions of GitLab.
 *GitLab Enterprise Edition (EE)* includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users.
 To get access to the EE and support please [become a subscriber](https://about.gitlab.com/pricing/).
 
-## Canonical source
-
-- The source of GitLab Enterprise Edition is [hosted on GitLab.com](https://dev.gitlab.org/gitlab/gitlab-ee/) and acessible only to [subscribers](https://about.gitlab.com/subscription/).
-
 ## Code status
 
 - [![build status](https://ci.gitlab.org/projects/1/status.png?ref=master)](https://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
diff --git a/VERSION b/VERSION
index 990b853f77b274b63a2edffb892c226066838e16..6aed29f5601ace782c81501d2815e3cfd1b0db33 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-7.10.0.pre-ee
+7.11.0.pre-ee
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 020c103dbc525414c56c05a793ec7b0ff4df5e64..bb9da1470180b51828cc5f3241533d126eb2a55b 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -132,10 +132,17 @@ $ ->
     ), 1
 
   # Initialize tooltips
-  $('.has_tooltip').tooltip()
-
-  # Bottom tooltip
-  $('.has_bottom_tooltip').tooltip(placement: 'bottom')
+  $('body').tooltip({
+    selector: '.has_tooltip, [data-toggle="tooltip"], .page-sidebar-collapsed .nav-sidebar a'
+    placement: (_, el) ->
+      $el = $(el)
+      if $el.attr('id') == 'js-shortcuts-home'
+        # Place the logo tooltip on the right when collapsed, bottom when expanded
+        $el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom'
+      else
+        # Otherwise use the data-placement attribute like normal
+        $el.data('placement')
+  })
 
   # Form submitter
   $('.trigger-submit').on 'change', ->
diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee
index 00d56ae5b4b0de2d24c87d34e4b072c9a3ab56c8..4eb3f3c03f310a9a50276709ae0b639154b37791 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.coffee
+++ b/app/assets/javascripts/gfm_auto_complete.js.coffee
@@ -2,19 +2,19 @@
 
 window.GitLab ?= {}
 GitLab.GfmAutoComplete =
-  # private_token: ''
   dataSource: ''
+
   # Emoji
   Emoji:
-    template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>'
+    template: '<li>${name} <img alt="${name}" height="20" src="${path}" width="20" /></li>'
 
   # Team Members
   Members:
-    template: '<li data-value="${username}">${username} <small>${name}</small></li>'
+    template: '<li>${username} <small>${name}</small></li>'
 
   # Issues and MergeRequests
   Issues:
-    template: '<li data-value="${id}"><small>${id}</small> ${title} </li>'
+    template: '<li><small>${id}</small> ${title}</li>'
 
   # Add GFM auto-completion to all input fields, that accept GFM input.
   setup: ->
@@ -23,45 +23,46 @@ GitLab.GfmAutoComplete =
     # Emoji
     input.atwho
       at: ':'
-      tpl: @Emoji.template
-      callbacks:
-        before_save: (emojis) =>
-          $.map emojis, (em) => name: em.name, insert: em.name+ ':', image: em.path
+      displayTpl: @Emoji.template
+      insertTpl: ':${name}:'
 
     # Team Members
     input.atwho
       at: '@'
-      tpl: @Members.template
-      search_key: 'search'
+      displayTpl: @Members.template
+      insertTpl: '${atwho-at}${username}'
+      searchKey: 'search'
       callbacks:
-        before_save: (members) =>
-          $.map members, (m) => name: m.name, username: m.username, search: "#{m.username} #{m.name}"
+        beforeSave: (members) ->
+          $.map members, (m) -> name: m.name, username: m.username, search: "#{m.username} #{m.name}"
 
     input.atwho
       at: '#'
       alias: 'issues'
-      search_key: 'search'
-      tpl: @Issues.template
+      searchKey: 'search'
+      displayTpl: @Issues.template
+      insertTpl: '${atwho-at}${id}'
       callbacks:
-        before_save: (issues) ->
+        beforeSave: (issues) ->
           $.map issues, (i) -> id: i.iid, title: sanitize(i.title), search: "#{i.iid} #{i.title}"
 
     input.atwho
       at: '!'
       alias: 'mergerequests'
-      search_key: 'search'
-      tpl: @Issues.template
+      searchKey: 'search'
+      displayTpl: @Issues.template
+      insertTpl: '${atwho-at}${id}'
       callbacks:
-        before_save: (merges) ->
+        beforeSave: (merges) ->
           $.map merges, (m) -> id: m.iid, title: sanitize(m.title), search: "#{m.iid} #{m.title}"
 
-    input.one "focus", =>
+    input.one 'focus', =>
       $.getJSON(@dataSource).done (data) ->
         # load members
-        input.atwho 'load', "@", data.members
+        input.atwho 'load', '@', data.members
         # load issues
-        input.atwho 'load', "issues", data.issues
+        input.atwho 'load', 'issues', data.issues
         # load merge requests
-        input.atwho 'load', "mergerequests", data.mergerequests
+        input.atwho 'load', 'mergerequests', data.mergerequests
         # load emojis
-        input.atwho 'load', ":", data.emojis
+        input.atwho 'load', ':', data.emojis
diff --git a/app/assets/stylesheets/base/gl_bootstrap.scss b/app/assets/stylesheets/base/gl_bootstrap.scss
index 62a3eade5c73a4c54eaf297e1722d54eb0d61323..427f333423c6271bb4a4abba5e211375aa00ebaf 100644
--- a/app/assets/stylesheets/base/gl_bootstrap.scss
+++ b/app/assets/stylesheets/base/gl_bootstrap.scss
@@ -117,7 +117,7 @@
         color: #888;
         text-shadow: 0 1px 1px #fff;
       }
-      i[class~="fa"] {
+      i.fa {
         line-height: 14px;
       }
     }
diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/base/mixins.scss
index feff931156cb7c55c7891fedeaf02664c5e3d61f..b8b163a42b286c25c449820c6d68577419105d97 100644
--- a/app/assets/stylesheets/base/mixins.scss
+++ b/app/assets/stylesheets/base/mixins.scss
@@ -106,7 +106,6 @@
   p > code {
     font-size: inherit;
     font-weight: inherit;
-    color: #555;
   }
 
   li {
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss
index 9558f241b7c7e96c3f93547dc4571ed38e3cb386..869e586839ba22c565cdaeb5ad81f644a6797182 100644
--- a/app/assets/stylesheets/generic/issue_box.scss
+++ b/app/assets/stylesheets/generic/issue_box.scss
@@ -6,7 +6,7 @@
 
 .issue-box {
   display: inline-block;
-  padding: 7px 13px;
+  padding: 4px 13px;
   font-weight: normal;
   margin-right: 5px;
 
diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss
index 71a1fc4493f4cccf0213b2a3e4697100b1586a2b..c9bbacd7348efe6936cd6c754582afc7712c6ed8 100644
--- a/app/assets/stylesheets/generic/mobile.scss
+++ b/app/assets/stylesheets/generic/mobile.scss
@@ -4,6 +4,11 @@
     margin-top: 20px;
   }
 
+  .container-fluid {
+    padding-left: 5px;
+    padding-right: 5px;
+  }
+
   .nav.nav-tabs > li > a {
     padding: 10px;
     font-size: 12px;
@@ -27,6 +32,34 @@
   .project-home-links {
     display: none;
   }
+
+  .project-avatar {
+    display: none;
+  }
+
+  .project-home-panel {
+    padding-left: 0 !important;
+
+    .project-home-row {
+      .project-home-desc {
+        margin-right: 0 !important;
+        float: none !important;
+      }
+
+      .project-repo-buttons {
+        position: static;
+        margin-top: 15px;
+        width: 100%;
+        float: none;
+        text-align: left;
+      }
+    }
+  }
+
+  .navbar-inner .title {
+    margin-left: 6px !important;
+    max-width: 70% !important;
+  }
 }
 
 @media (max-width: $screen-sm-max) {
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index cd86a9be8b2753f2f472fa79e29d771eb4f1b752..3572f33e91fff979bd34a08c25bfc48d55cea041 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -25,17 +25,8 @@
       display: inline-block;
     }
 
-    .issue-actions {
-      display: none;
-      position: absolute;
-      top: 10px;
-      right: 15px;
-    }
-
-    &:hover {
-      .issue-actions {
-        display: block;
-      }
+    .issue-no-comments {
+      opacity: 0.5;
     }
   }
 }
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 8abd4207beb15f41f2761f07f36edb7d3871520e..3165396a94d212034db1f3d2ba56465574b4644e 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -91,11 +91,16 @@
     .merge-request-info {
       color: #999;
       font-size: 13px;
-
-      .merge-request-labels {
-        display: inline-block;
-      }
     }
+
+  }
+
+  .merge-request-labels {
+    display: inline-block;
+  }
+
+  .merge-request-no-comments {
+    opacity: 0.5;
   }
 }
 
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index facd7e193146ad66f4783f58cc9147924eb46ef2..97b19deb3edcc67019e26fc59850235fc9e29b48 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -136,7 +136,7 @@ ul.notes {
     display: none;
     float: right;
 
-    [class~="fa"] {
+    i.fa {
       font-size: 16px;
       line-height: 16px;
       vertical-align: middle;
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 6e19f97ce060750c14e8812fbd23cfd4f0a6a4d7..657d70d726222c1d082cfbf63357dabb93d5281a 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -40,6 +40,8 @@ def application_setting_params
       :home_page_url,
       :help_text,
       :max_attachment_size,
+      :default_project_visibility,
+      :default_snippet_visibility,
       restricted_visibility_levels: []
     )
   end
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index 0a463239d7496d7e27f8d7b1eb998ad1ab95df4f..690096bdbcf5ff05ae66d0ad4e61afcf61556313 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -33,7 +33,7 @@ def test
       owner_name: "Someone",
       owner_email: "example@gitlabhq.com"
     }
-    @hook.execute(data)
+    @hook.execute(data, 'system_hooks')
 
     redirect_to :back
   end
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index c1fdcd7fab66ef698154f668b731792276e7cbda..a62170662e12c34070374c69f6396a616b82aeeb 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -40,15 +40,6 @@ def service
 
   def application_services_params
     params.permit(:id,
-      service: [
-        :title, :token, :type, :active, :api_key, :subdomain,
-        :room, :recipients, :project_url, :webhook,
-        :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
-        :build_key, :server, :teamcity_url, :build_type,
-        :description, :issues_url, :new_issue_url, :restrict_to_branch,
-        :send_from_committer_email, :disable_diffs,
-        :push_events, :tag_push_events, :note_events, :issues_events,
-        :merge_requests_events
-    ])
+      service: Projects::ServicesController::ALLOWED_PARAMS)
   end
 end
diff --git a/app/controllers/import/gitorious_controller.rb b/app/controllers/import/gitorious_controller.rb
index 6067a87ee04dbc019e4b013956ffcc8d2bb33e8e..c121d2de7cb65c4732e769355b0018826c28dd25 100644
--- a/app/controllers/import/gitorious_controller.rb
+++ b/app/controllers/import/gitorious_controller.rb
@@ -6,7 +6,7 @@ def new
 
   def callback
     session[:gitorious_repos] = params[:repos]
-    redirect_to status_import_gitorious_url
+    redirect_to status_import_gitorious_path
   end
 
   def status
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 8a1b7899be3d0172aeb088de81f8b7f71ef21e45..78d42d695b68e7404a7e5b42f7dba8369b82286f 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -10,11 +10,11 @@ class Projects::CommitController < Projects::ApplicationController
   def show
     return git_not_found! unless @commit
 
-    @line_notes = commit.notes(@project).inline
+    @line_notes = commit.notes.inline
     @diffs = @commit.diffs
     @note = @project.build_commit_note(commit)
-    @notes_count = commit.notes(@project).count
-    @notes = commit.notes(@project).not_inline.fresh
+    @notes_count = commit.notes.count
+    @notes = commit.notes.not_inline.fresh
     @noteable = @commit
     @comments_allowed = @reply_allowed = true
     @comments_target  = {
@@ -36,6 +36,6 @@ def branches
   end
 
   def commit
-    @commit ||= @project.repository.commit(params[:id])
+    @commit ||= @project.commit(params[:id])
   end
 end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index ab75f2ddb7351f5dd22007de9e6787134d24dcd2..41b4e55a598f2e270797b578d9cd0d1553e8932c 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -146,7 +146,7 @@ def branch_from
 
   def branch_to
     @target_project = selected_target_project
-    @commit = @target_project.repository.commit(params[:ref]) if params[:ref].present?
+    @commit = @target_project.commit(params[:ref]) if params[:ref].present?
   end
 
   def update_branches
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 2c3209def7ce0080bf480bd2366b02022d109eed..3f9b578793efde167156f62edc40806d6440e71e 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -1,4 +1,14 @@
 class Projects::ServicesController < Projects::ApplicationController
+  ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :subdomain,
+                    :room, :recipients, :project_url, :webhook,
+                    :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
+                    :build_key, :server, :teamcity_url, :build_type,
+                    :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
+                    :colorize_messages, :channels,
+                    :push_events, :issues_events, :merge_requests_events, :tag_push_events,
+                    :note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url,
+                    :jira_issue_transition_id, :notify, :color]
+
   # Authorize
   before_action :authorize_admin_project!
   before_action :service, only: [:edit, :update, :test]
@@ -45,16 +55,6 @@ def service
   end
 
   def service_params
-    params.require(:service).permit(
-      :title, :token, :type, :active, :api_key, :subdomain,
-      :room, :recipients, :project_url, :webhook,
-      :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
-      :build_key, :server, :teamcity_url, :build_type,
-      :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
-      :colorize_messages, :channels,
-      :push_events, :issues_events, :merge_requests_events, :tag_push_events,
-      :note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url,
-      :jira_issue_transition_id
-    )
+    params.require(:service).permit(ALLOWED_PARAMS)
   end
 end
diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb
index a35adeac6f039eae1ff22b755621747d27768383..e2d0b0d945908c9746c7a94e4de8422b98ea675a 100644
--- a/app/controllers/projects/uploads_controller.rb
+++ b/app/controllers/projects/uploads_controller.rb
@@ -1,11 +1,8 @@
 class Projects::UploadsController < Projects::ApplicationController
   layout 'project'
 
-  # We want to skip these filters for only the `show` action if `image?` is true,
-  # but `skip_before_action` doesn't work with both `only` and `if`, so we accomplish the same like this.
-  skipped_filters = [:authenticate_user!, :reject_blocked!, :project, :repository]
-  skip_before_action  *skipped_filters, only: [:show]
-  before_action       *skipped_filters, only: [:show], unless: :image?
+  skip_before_action :authenticate_user!, :reject_blocked!, :project,
+    :repository, if: -> { action_name == 'show' && image? }
 
   def create
     link_to_file = ::Projects::UploadService.new(project, params[:file]).
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 0be3fdb058b57a08e2c9c45dd09d975c68259e3b..936a014fb9ed0117f5a323f3c8e7b75e17b9315e 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -66,8 +66,6 @@ def show
       return
     end
 
-    limit = (params[:limit] || 20).to_i
-
     @show_star = !(current_user && current_user.starred?(@project))
 
     respond_to do |format|
@@ -85,11 +83,14 @@ def show
       end
 
       format.json do
-        @events = @project.events.recent
-        @events = event_filter.apply_filter(@events).with_associations
-        @events = @events.limit(limit).offset(params[:offset] || 0)
+        load_events
         pager_json('events/_events', @events.count)
       end
+
+      format.atom do
+        load_events
+        render layout: false
+      end
     end
   end
 
@@ -167,6 +168,13 @@ def user_layout
     current_user ? 'projects' : 'public_projects'
   end
 
+  def load_events
+    @events = @project.events.recent
+    @events = event_filter.apply_filter(@events).with_associations
+    limit = (params[:limit] || 20).to_i
+    @events = @events.limit(limit).offset(params[:offset] || 0)
+  end
+
   def project_params
     params.require(:project).permit(
       :name, :path, :description, :issues_tracker, :tag_list,
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index a6844b2a47b84221d4ad5d724a798ecef05e3cd2..6e86400a4f6e4c3bd9a5d4db8bf3508b5a01688a 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -318,4 +318,18 @@ def path_to_key(key, admin = false)
       profile_key_path(key)
     end
   end
+
+  def state_filters_text_for(entity, project)
+    entity_title = entity.to_s.humanize
+
+    count =
+      if project.nil?
+        ""
+      elsif current_controller?(:issues)
+        " (#{project.issues.send(entity).count})"
+      elsif current_controller?(:merge_requests)
+        " (#{project.merge_requests.send(entity).count})"
+      end
+    "#{entity_title}#{count}"
+  end
 end
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index 0d573e72a80aa93807d869b1436b5fdd070eda13..66a1383d61b380b6fe974ca2a380de7b4eea0b89 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -10,7 +10,21 @@ def visibility_level_color(level)
     end
   end
 
-  def visibility_level_description(level)
+  # Return the description for the +level+ argument.
+  #
+  # +level+ One of the Gitlab::VisibilityLevel constants
+  # +form_model+ Either a model object (Project, Snippet, etc.) or the name of
+  #              a Project or Snippet class.
+  def visibility_level_description(level, form_model)
+    case form_model.is_a?(String) ? form_model : form_model.class.name
+    when 'PersonalSnippet', 'ProjectSnippet', 'Snippet'
+      snippet_visibility_level_description(level)
+    when 'Project'
+      project_visibility_level_description(level)
+    end
+  end
+
+  def project_visibility_level_description(level)
     capture_haml do
       haml_tag :span do
         case level
@@ -64,4 +78,12 @@ def restricted_visibility_levels(show_all = false)
     return [] if current_user.is_admin? && !show_all
     current_application_settings.restricted_visibility_levels || []
   end
+
+  def default_project_visibility
+    current_application_settings.default_project_visibility
+  end
+
+  def default_snippet_visibility
+    current_application_settings.default_snippet_visibility
+  end
 end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index 0dbb2939bb30c33702d54cd8c4225c955908bc84..9cb7077e59d37d0317b6b995ca4bd347ede0b2d5 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -79,7 +79,7 @@ def repository_push_email(project_id, recipient,  author_id: nil,
       @disable_diffs = disable_diffs
 
       if @compare
-        @commits = Commit.decorate(compare.commits)
+        @commits = Commit.decorate(compare.commits, @project)
         @diffs   = compare.diffs
       end
 
@@ -101,8 +101,8 @@ def repository_push_email(project_id, recipient,  author_id: nil,
         if @commits.length > 1
           @target_url = namespace_project_compare_url(@project.namespace,
                                                       @project,
-                                                      from: Commit.new(@compare.base),
-                                                      to:   Commit.new(@compare.head))
+                                                      from: Commit.new(@compare.base, @project),
+                                                      to:   Commit.new(@compare.head, @project))
           @subject << "Deleted " if @reverse_compare
           @subject << "#{@commits.length} commits: #{@commits.first.title}"
         else
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 97a486c952b85597bdce1b2ce1c931c00090cf69..9a90a8e4673ff41dc092728ec661abdb249a6452 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -18,6 +18,8 @@
 #  help_text                    :text
 #  restricted_visibility_levels :text
 #  max_attachment_size          :integer          default(10)
+#  default_project_visibility   :integer
+#  default_snippet_visibility   :integer
 #
 
 class ApplicationSetting < ActiveRecord::Base
@@ -52,7 +54,9 @@ def self.create_from_defaults
       gravatar_enabled: Settings.gravatar['enabled'],
       sign_in_text: Settings.extra['sign_in_text'],
       restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
-      max_attachment_size: Settings.gitlab['max_attachment_size']
+      max_attachment_size: Settings.gitlab['max_attachment_size'],
+      default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
+      default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level']
     )
   end
 
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 1cabc060c2affd7afd6ca56864a7aa74b5d76183..be5a118bfecfe7a1ae53180b52285dc1f2ab91da 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -3,8 +3,12 @@ class Commit
   include StaticModel
   extend ActiveModel::Naming
   include Mentionable
+  include Participable
 
   attr_mentionable :safe_message
+  participant :author, :committer, :notes, :mentioned_users
+
+  attr_accessor :project
 
   # Safe amount of changes (files and lines) in one commit to render
   # Used to prevent 500 error on huge commits by suppressing diff
@@ -18,12 +22,12 @@ class Commit
   DIFF_HARD_LIMIT_LINES = 50000 unless defined?(DIFF_HARD_LIMIT_LINES)
 
   class << self
-    def decorate(commits)
+    def decorate(commits, project)
       commits.map do |commit|
         if commit.kind_of?(Commit)
           commit
         else
-          self.new(commit)
+          self.new(commit, project)
         end
       end
     end
@@ -41,10 +45,11 @@ def truncate_sha(sha)
 
   attr_accessor :raw
 
-  def initialize(raw_commit)
+  def initialize(raw_commit, project)
     raise "Nil as raw commit passed" unless raw_commit
 
     @raw = raw_commit
+    @project = project
   end
 
   def id
@@ -100,7 +105,7 @@ def description?
     description.present?
   end
 
-  def hook_attrs(project)
+  def hook_attrs
     path_with_namespace = project.path_with_namespace
 
     {
@@ -117,7 +122,7 @@ def hook_attrs(project)
 
   # Discover issues should be closed when this commit is pushed to a project's
   # default branch.
-  def closes_issues(project, current_user = self.committer)
+  def closes_issues(current_user = self.committer)
     Gitlab::ClosingIssueExtractor.new(project, current_user).closed_by_message(safe_message)
   end
 
@@ -134,22 +139,7 @@ def committer
     User.find_for_commit(committer_email, committer_name)
   end
 
-  def participants(project, current_user = nil)
-    users = []
-    users << author
-    users << committer
-    
-    users.push *self.mentioned_users(current_user, project)
-
-    notes(project).each do |note|
-      users << note.author
-      users.push *note.mentioned_users(current_user, project)
-    end
-
-    users.uniq
-  end
-
-  def notes(project)
+  def notes
     project.notes.for_commit_id(self.id)
   end
 
@@ -169,6 +159,6 @@ def short_id
   end
 
   def parents
-    @parents ||= Commit.decorate(super)
+    @parents ||= Commit.decorate(super, project)
   end
 end
diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e64561982645ca4fc8ca0c7a757570a0b5359b5b
--- /dev/null
+++ b/app/models/commit_range.rb
@@ -0,0 +1,106 @@
+# CommitRange makes it easier to work with commit ranges
+#
+# Examples:
+#
+#   range = CommitRange.new('f3f85602...e86e1013')
+#   range.exclude_start?  # => false
+#   range.reference_title # => "Commits f3f85602 through e86e1013"
+#   range.to_s            # => "f3f85602...e86e1013"
+#
+#   range = CommitRange.new('f3f856029bc5f966c5a7ee24cf7efefdd20e6019..e86e1013709735be5bb767e2b228930c543f25ae')
+#   range.exclude_start?  # => true
+#   range.reference_title # => "Commits f3f85602^ through e86e1013"
+#   range.to_param        # => {from: "f3f856029bc5f966c5a7ee24cf7efefdd20e6019^", to: "e86e1013709735be5bb767e2b228930c543f25ae"}
+#   range.to_s            # => "f3f85602..e86e1013"
+#
+#   # Assuming `project` is a Project with a repository containing both commits:
+#   range.project = project
+#   range.valid_commits? # => true
+#
+class CommitRange
+  include ActiveModel::Conversion
+
+  attr_reader :sha_from, :notation, :sha_to
+
+  # Optional Project model
+  attr_accessor :project
+
+  # See `exclude_start?`
+  attr_reader :exclude_start
+
+  # The beginning and ending SHA sums can be between 6 and 40 hex characters,
+  # and the range selection can be double- or triple-dot.
+  PATTERN = /\h{6,40}\.{2,3}\h{6,40}/
+
+  # Initialize a CommitRange
+  #
+  # range_string - The String commit range.
+  # project      - An optional Project model.
+  #
+  # Raises ArgumentError if `range_string` does not match `PATTERN`.
+  def initialize(range_string, project = nil)
+    range_string.strip!
+
+    unless range_string.match(/\A#{PATTERN}\z/)
+      raise ArgumentError, "invalid CommitRange string format: #{range_string}"
+    end
+
+    @exclude_start = !range_string.include?('...')
+    @sha_from, @notation, @sha_to = range_string.split(/(\.{2,3})/, 2)
+
+    @project = project
+  end
+
+  def inspect
+    %(#<#{self.class}:#{object_id} #{to_s}>)
+  end
+
+  def to_s
+    "#{sha_from[0..7]}#{notation}#{sha_to[0..7]}"
+  end
+
+  # Returns a String for use in a link's title attribute
+  def reference_title
+    "Commits #{suffixed_sha_from} through #{sha_to}"
+  end
+
+  # Return a Hash of parameters for passing to a URL helper
+  #
+  # See `namespace_project_compare_url`
+  def to_param
+    { from: suffixed_sha_from, to: sha_to }
+  end
+
+  def exclude_start?
+    exclude_start
+  end
+
+  # Check if both the starting and ending commit IDs exist in a project's
+  # repository
+  #
+  # project - An optional Project to check (default: `project`)
+  def valid_commits?(project = project)
+    return nil   unless project.present?
+    return false unless project.valid_repo?
+
+    commit_from.present? && commit_to.present?
+  end
+
+  def persisted?
+    true
+  end
+
+  def commit_from
+    @commit_from ||= project.repository.commit(suffixed_sha_from)
+  end
+
+  def commit_to
+    @commit_to ||= project.repository.commit(sha_to)
+  end
+
+  private
+
+  def suffixed_sha_from
+    sha_from + (exclude_start? ? '^' : '')
+  end
+end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index a21d9bdfe8aae4a74fdaf82e88a3d81fc8a5f141..97846b06d72a3b98074c8194cafe41fc357bb6ac 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -7,6 +7,7 @@
 module Issuable
   extend ActiveSupport::Concern
   include Mentionable
+  include Participable
 
   included do
     belongs_to :author, class_name: "User"
@@ -45,6 +46,7 @@ module Issuable
              prefix: true
 
     attr_mentionable :title, :description
+    participant :author, :assignee, :notes, :mentioned_users
   end
 
   module ClassMethods
@@ -117,22 +119,6 @@ def votes_count
     upvotes + downvotes
   end
 
-  # Return all users participating on the discussion
-  def participants(current_user = self.author)
-    users = []
-    users << author
-    users << assignee if is_assigned?
-
-    users.push *self.mentioned_users(current_user)
-
-    notes.each do |note|
-      users << note.author
-      users.push *note.mentioned_users(current_user)
-    end
-
-    users.uniq
-  end
-
   def subscribed?(user)
     subscription = subscriptions.find_by_user_id(user.id)
 
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index aad5e51479381e4ee1da386eaecc26bb00c9592a..3ef3e8b67d879ed9c2e0a9ab8941f9c62d735745 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -64,7 +64,7 @@ def references(p = project, current_user = self.author, text = mentionable_text)
   def create_cross_references!(p = project, a = author, without = [])
     refs = references(p) - without
     refs.each do |ref|
-      Note.create_cross_reference_note(ref, local_reference, a, p)
+      Note.create_cross_reference_note(ref, local_reference, a)
     end
   end
 
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7a5e4876ff24cbe785c8c31c1d743a8bcad9aaa0
--- /dev/null
+++ b/app/models/concerns/participable.rb
@@ -0,0 +1,65 @@
+# == Participable concern
+#
+# Contains functionality related to objects that can have participants, such as
+# an author, an assignee and people mentioned in its description or comments.
+#
+# Used by Issue, Note, MergeRequest, Snippet and Commit.
+#
+# Usage:
+#
+#     class Issue < ActiveRecord::Base
+#       include Participable
+#
+#       # ...
+#
+#       participant :author, :assignee, :mentioned_users, :notes
+#     end
+#     
+#     issue = Issue.last
+#     users = issue.participants
+#     # `users` will contain the issue's author, its assignee,
+#     # all users returned by its #mentioned_users method,
+#     # as well as all participants to all of the issue's notes,
+#     # since Note implements Participable as well.
+#
+module Participable
+  extend ActiveSupport::Concern
+
+  module ClassMethods
+    def participant(*attrs)
+      participant_attrs.concat(attrs.map(&:to_s))
+    end
+
+    def participant_attrs
+      @participant_attrs ||= []
+    end
+  end
+
+  def participants(current_user = self.author)
+    self.class.participant_attrs.flat_map do |attr|
+      meth = method(attr)
+
+      value = 
+        if meth.arity == 1
+          meth.call(current_user)
+        else
+          meth.call
+        end
+
+      participants_for(value, current_user)
+    end.compact.uniq
+  end
+
+  private
+  
+  def participants_for(value, current_user = nil)
+    case value
+    when User
+      [value]
+    when Enumerable, ActiveRecord::Relation
+      value.flat_map { |v| participants_for(v, current_user) }
+    when Participable
+      value.participants(current_user)
+    end
+  end
+end
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 315d96af1b9fa04bd8de97d44cff861fc81429d8..e9fd441352d85535c0f341f2c18069428da15c83 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -30,12 +30,15 @@ class WebHook < ActiveRecord::Base
   validates :url, presence: true,
                   format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }
 
-  def execute(data)
+  def execute(data, hook_name)
     parsed_url = URI.parse(url)
     if parsed_url.userinfo.blank?
       WebHook.post(url,
                    body: data.to_json,
-                   headers: { "Content-Type" => "application/json" },
+                   headers: {
+                     "Content-Type" => "application/json",
+                     "X-Gitlab-Event" => hook_name.singularize.titleize
+                   },
                    verify: false)
     else
       post_url = url.gsub("#{parsed_url.userinfo}@", "")
@@ -45,7 +48,10 @@ def execute(data)
       }
       WebHook.post(post_url,
                    body: data.to_json,
-                   headers: { "Content-Type" => "application/json" },
+                   headers: {
+                     "Content-Type" => "application/json",
+                     "X-Gitlab-Event" => hook_name.singularize.titleize
+                   },
                    verify: false,
                    basic_auth: auth)
     end
@@ -54,7 +60,7 @@ def execute(data)
     false
   end
 
-  def async_execute(data)
-    Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data)
+  def async_execute(data, hook_name)
+    Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data, hook_name)
   end
 end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index b92834e5e3f2bc91cf0b646a13ff4c0860329de0..fab3fb259f2881647b8a1a690578e49c68a6c012 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -216,10 +216,13 @@ def mr_and_commit_notes
     commits_for_notes_limit = 100
     commit_ids = commits.last(commits_for_notes_limit).map(&:id)
 
-    project.notes.where(
-      "(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
+    Note.where(
+      "(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" +
+      "(project_id = :source_project_id AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
       mr_id: id,
-      commit_ids: commit_ids
+      commit_ids: commit_ids,
+      target_project_id: target_project_id,
+      source_project_id: source_project_id
     )
   end
 
@@ -245,7 +248,7 @@ def hook_attrs
     }
 
     unless last_commit.nil?
-      attrs.merge!(last_commit: last_commit.hook_attrs(source_project))
+      attrs.merge!(last_commit: last_commit.hook_attrs)
     end
 
     attributes.merge!(attrs)
@@ -262,7 +265,7 @@ def project
   # Return the set of issues that will be closed if this merge request is accepted.
   def closes_issues(current_user = self.author)
     if target_branch == project.default_branch
-      issues = commits.flat_map { |c| c.closes_issues(project, current_user) }
+      issues = commits.flat_map { |c| c.closes_issues(current_user) }
       issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user).
                   closed_by_message(description))
       issues.uniq.sort_by(&:id)
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index acac1ca4cf7ca2e3ccabbf70e754f6402d730e19..df1c2b78758225484d2e73c5615a386c784ee266 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -67,7 +67,7 @@ def dump_commits(commits)
   end
 
   def load_commits(array)
-    array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash)) }
+    array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash), merge_request.source_project) }
   end
 
   def dump_diffs(diffs)
@@ -88,7 +88,7 @@ def unmerged_commits
     commits = compare_result.commits
 
     if commits.present?
-      commits = Commit.decorate(commits).
+      commits = Commit.decorate(commits, merge_request.source_project).
         sort_by(&:created_at).
         reverse
     end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index e1de114375e63ce012d3d5eb93d57bf99a30322a..211dfa76b811f34ffe9f8763d4f7b185d057d95a 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -60,15 +60,24 @@ def search(query)
 
     def clean_path(path)
       path = path.dup
+      # Get the email username by removing everything after an `@` sign.
       path.gsub!(/@.*\z/,             "")
+      # Usernames can't end in .git, so remove it.
       path.gsub!(/\.git\z/,           "")
+      # Remove dashes at the start of the username.
       path.gsub!(/\A-+/,              "")
+      # Remove periods at the end of the username.
       path.gsub!(/\.+\z/,             "")
+      # Remove everything that's not in the list of allowed characters.
       path.gsub!(/[^a-zA-Z0-9_\-\.]/, "")
 
+      # Users with the great usernames of "." or ".." would end up with a blank username.
+      # Work around that by setting their username to "blank", followed by a counter. 
+      path = "blank" if path.blank?
+
       counter = 0
       base = path
-      while Namespace.by_path(path).present?
+      while Namespace.find_by_path_or_name(path)
         counter += 1
         path = "#{base}#{counter}"
       end
diff --git a/app/models/note.rb b/app/models/note.rb
index 8df55237dc5bf008c8fb65724f7d78660d7eed2b..c06dc1579fbad9a0fd468c770459c91197f97b9d 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -23,10 +23,12 @@
 class Note < ActiveRecord::Base
   include Mentionable
   include Gitlab::CurrentSettings
+  include Participable
 
   default_value_for :system, false
 
   attr_mentionable :note
+  participant :author, :mentioned_users
 
   belongs_to :project
   belongs_to :noteable, polymorphic: true
@@ -77,11 +79,11 @@ def create_status_change_note(noteable, project, author, status, source)
     # +mentioner+'s description or an associated Note.
     # Create a system Note associated with +noteable+ with a GFM back-reference
     # to +mentioner+.
-    def create_cross_reference_note(noteable, mentioner, author, project)
-      gfm_reference = mentioner_gfm_ref(noteable, mentioner, project)
+    def create_cross_reference_note(noteable, mentioner, author)
+      gfm_reference = mentioner_gfm_ref(noteable, mentioner)
 
       note_options = {
-        project: project,
+        project: noteable.project,
         author: author,
         note: cross_reference_note_content(gfm_reference),
         system: true
@@ -241,7 +243,7 @@ def cross_reference_disallowed?(noteable, mentioner)
 
     # Determine whether or not a cross-reference note already exists.
     def cross_reference_exists?(noteable, mentioner)
-      gfm_reference = mentioner_gfm_ref(noteable, mentioner)
+      gfm_reference = mentioner_gfm_ref(noteable, mentioner, true)
       notes = if noteable.is_a?(Commit)
                 where(commit_id: noteable.id, noteable_type: 'Commit')
               else
@@ -274,43 +276,19 @@ def cross_reference_note_pattern(gfm_reference)
     # Prepend the mentioner's namespaced project path to the GFM reference for
     # cross-project references.  For same-project references, return the
     # unmodified GFM reference.
-    def mentioner_gfm_ref(noteable, mentioner, project = nil)
-      if mentioner.is_a?(Commit)
-        if project.nil?
-          return mentioner.gfm_reference.sub('commit ', 'commit %')
-        else
-          mentioning_project = project
-        end
-      else
-        mentioning_project = mentioner.project
-      end
-
-      noteable_project_id = noteable_project_id(noteable, mentioning_project)
-
-      full_gfm_reference(mentioning_project, noteable_project_id, mentioner)
-    end
-
-    # Return the ID of the project that +noteable+ belongs to, or nil if
-    # +noteable+ is a commit and is not part of the project that owns
-    # +mentioner+.
-    def noteable_project_id(noteable, mentioning_project)
-      if noteable.is_a?(Commit)
-        if mentioning_project.repository.commit(noteable.id)
-          # The noteable commit belongs to the mentioner's project
-          mentioning_project.id
-        else
-          nil
-        end
-      else
-        noteable.project.id
+    def mentioner_gfm_ref(noteable, mentioner, cross_reference = false)
+      if mentioner.is_a?(Commit) && cross_reference
+        return mentioner.gfm_reference.sub('commit ', 'commit %')
       end
+      
+      full_gfm_reference(mentioner.project, noteable.project, mentioner)
     end
 
     # Return the +mentioner+ GFM reference.  If the mentioner and noteable
     # projects are not the same, add the mentioning project's path to the
     # returned value.
-    def full_gfm_reference(mentioning_project, noteable_project_id, mentioner)
-      if mentioning_project.id == noteable_project_id
+    def full_gfm_reference(mentioning_project, noteable_project, mentioner)
+      if mentioning_project == noteable_project
         mentioner.gfm_reference
       else
         if mentioner.is_a?(Commit)
@@ -517,7 +495,7 @@ def for_project_snippet?
   # override to return commits, which are not active record
   def noteable
     if for_commit?
-      project.repository.commit(commit_id)
+      project.commit(commit_id)
     else
       super
     end
diff --git a/app/models/project.rb b/app/models/project.rb
index 46713511e53285c16ea50760998b648cdff9f28e..e6a9cd0c3e4aae2530feba74f9dcc244db9676cb 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -259,7 +259,11 @@ def team
   end
 
   def repository
-    @repository ||= Repository.new(path_with_namespace)
+    @repository ||= Repository.new(path_with_namespace, nil, self)
+  end
+
+  def commit(id = 'HEAD')
+    repository.commit(id)
   end
 
   def saved?
@@ -492,7 +496,7 @@ def path_with_namespace
 
   def execute_hooks(data, hooks_scope = :push_hooks)
     hooks.send(hooks_scope).each do |hook|
-      hook.async_execute(data)
+      hook.async_execute(data, hooks_scope.to_s)
     end
     if group
       group.hooks.send(hooks_scope).each do |hook|
@@ -708,11 +712,21 @@ def group_ldap_synced?
   end
 
   def create_repository
-    if gitlab_shell.add_repository(path_with_namespace)
-      true
+    if forked?
+      if gitlab_shell.fork_repository(forked_from_project.path_with_namespace, self.namespace.path)
+        ensure_satellite_exists
+        true
+      else
+        errors.add(:base, 'Failed to fork repository')
+        false
+      end
     else
-      errors.add(:base, 'Failed to create repository')
-      false
+      if gitlab_shell.add_repository(path_with_namespace)
+        true
+      else
+        errors.add(:base, 'Failed to create repository')
+        false
+      end
     end
   end
 
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 07520eab5d13164793824b186ecb693adb4314cd..2fa5f0ce71c275c21f56baaa6a594c27f9ed5849 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -20,7 +20,7 @@
 class HipchatService < Service
   MAX_COMMITS = 3
 
-  prop_accessor :token, :room, :server
+  prop_accessor :token, :room, :server, :notify, :color
   validates :token, presence: true, if: :activated?
 
   def title
@@ -39,6 +39,8 @@ def fields
     [
       { type: 'text', name: 'token',     placeholder: 'Room token' },
       { type: 'text', name: 'room',      placeholder: 'Room name or ID' },
+      { type: 'checkbox', name: 'notify' },
+      { type: 'select', name: 'color', choices: ['yellow', 'red', 'green', 'purple', 'gray', 'random'] },
       { type: 'text', name: 'server',
         placeholder: 'Leave blank for default. https://hipchat.example.com' }
     ]
@@ -52,7 +54,7 @@ def execute(data)
     return unless supported_events.include?(data[:object_kind])
     message = create_message(data)
     return unless message.present?
-    gate[room].send('GitLab', message)
+    gate[room].send('GitLab', message, message_options)
   end
 
   private
@@ -63,6 +65,10 @@ def gate
     @gate ||= HipChat::Client.new(token, options)
   end
 
+  def message_options
+    { notify: notify.present? && notify == '1', color: color || 'yellow' }
+  end
+
   def create_message(data)
     object_kind = data[:object_kind]
 
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 772c868d9cd0088307f7bab85d8166878a528a1c..0706a1ca0d12d485de81e6052e3f7e71fa38b7c5 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -112,7 +112,7 @@ def search_files(query)
   end
 
   def repository
-    Repository.new(path_with_namespace, default_branch)
+    Repository.new(path_with_namespace, default_branch, @project)
   end
 
   def default_branch
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 97207ba12726c4cd6179f6e623d5a447ff0de39f..8ebd790a89e1be20e12c4a6764f1cc4709a4441e 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -18,6 +18,6 @@ class ProtectedBranch < ActiveRecord::Base
   validates :project, presence: true
 
   def commit
-    project.repository.commit(self.name)
+    project.commit(self.name)
   end
 end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 263a436d5210469c5566684a5fbb2b7a52129f7c..1b8c74028d9aa1f6d27bab3717f972a488566d3b 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -1,11 +1,12 @@
 class Repository
   include Gitlab::ShellAdapter
 
-  attr_accessor :raw_repository, :path_with_namespace
+  attr_accessor :raw_repository, :path_with_namespace, :project
 
-  def initialize(path_with_namespace, default_branch = nil)
+  def initialize(path_with_namespace, default_branch = nil, project = nil)
     @path_with_namespace = path_with_namespace
     @raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace
+    @project = project
   rescue Gitlab::Git::Repository::NoRepository
     nil
   end
@@ -28,7 +29,7 @@ def empty?
   def commit(id = 'HEAD')
     return nil unless raw_repository
     commit = Gitlab::Git::Commit.find(raw_repository, id)
-    commit = Commit.new(commit) if commit
+    commit = Commit.new(commit, @project) if commit
     commit
   rescue Rugged::OdbError
     nil
@@ -42,13 +43,13 @@ def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
       limit: limit,
       offset: offset,
     )
-    commits = Commit.decorate(commits) if commits.present?
+    commits = Commit.decorate(commits, @project) if commits.present?
     commits
   end
 
   def commits_between(from, to)
     commits = Gitlab::Git::Commit.between(raw_repository, from, to)
-    commits = Commit.decorate(commits) if commits.present?
+    commits = Commit.decorate(commits, @project) if commits.present?
     commits
   end
 
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index c11c28805ebdd4a4c4cf5298451e95c02613daa0..d2af26539b6444627fb30f43ab13053d9ba2e54a 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -19,6 +19,7 @@ class Snippet < ActiveRecord::Base
   include Sortable
   include Linguist::BlobHelper
   include Gitlab::VisibilityLevel
+  include Participable
 
   default_value_for :visibility_level, Snippet::PRIVATE
 
@@ -47,6 +48,8 @@ class Snippet < ActiveRecord::Base
   scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) }
   scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) }
 
+  participant :author, :notes
+
   def self.content_types
     [
       ".rb", ".py", ".pl", ".scala", ".c", ".cpp", ".java",
@@ -87,18 +90,6 @@ def visibility_level_field
     visibility_level
   end
 
-  def participants(current_user = self.author)
-    users = []
-    users << author
-    
-    notes.each do |note|
-      users << note.author
-      users.push *note.mentioned_users(current_user)
-    end
-
-    users.uniq
-  end
-
   class << self
     def search(query)
       where('(title LIKE :query OR file_name LIKE :query)', query: "%#{query}%")
diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb
index 25f9e2032464665f4137b817f6ac2c12dc3cab96..1a7318048b3323ce8a32327cb7638c2d3e4570df 100644
--- a/app/services/create_tag_service.rb
+++ b/app/services/create_tag_service.rb
@@ -38,7 +38,7 @@ def success(branch)
   end
 
   def create_push_data(project, user, tag)
-    commits = [project.repository.commit(tag.target)].compact
+    commits = [project.commit(tag.target)].compact
     Gitlab::PushDataBuilder.
       build(project, user, Gitlab::Git::BLANK_SHA, tag.target, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", commits, tag.message)
   end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 25e5dbe619816490da25bda219ddb5e84b19f80f..e8274b5d367b077185a6a66567e450490f6bd33e 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -70,7 +70,7 @@ def process_commit_messages(ref)
       # Close issues if these commits were pushed to the project's default branch and the commit message matches the
       # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
       # a different branch.
-      issues_to_close = commit.closes_issues(project, user)
+      issues_to_close = commit.closes_issues(user)
 
       # Load commit author only if needed.
       # For push with 1k commits it prevents 900+ requests in database
@@ -98,7 +98,7 @@ def process_commit_messages(ref)
         author ||= commit_user(commit)
 
         refs.each do |r|
-          Note.create_cross_reference_note(r, commit, author, project)
+          Note.create_cross_reference_note(r, commit, author)
         end
       end
     end
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index bf203bbd69272c1f80103403ae87b6a7937abb02..075a6118da2054f285c90720f87dc4b392a04faa 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -25,7 +25,7 @@ def build_push_data(oldrev, newrev, ref)
       tag_name = Gitlab::Git.ref_name(ref)
       tag = project.repository.find_tag(tag_name)
       if tag && tag.target == newrev
-        commit = project.repository.commit(tag.target)
+        commit = project.commit(tag.target)
         commits = [commit].compact
         message = tag.message
       end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index 493b4bc1e415dfee106c4b492deecd460b2357ae..02503f0872768e68394fa045e40aac009911d04b 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -29,7 +29,7 @@ def execute
       # At this point we decide if merge request can be created
       # If we have at least one commit to merge -> creation allowed
       if commits.present?
-        merge_request.compare_commits = Commit.decorate(commits)
+        merge_request.compare_commits = Commit.decorate(commits, merge_request.source_project)
         merge_request.can_be_created = true
         merge_request.compare_failed = false
 
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index e969061f229d8a8ee6ce266cdf9a88a50c79af45..d19a6c2eca32bca8121117f0fc123c2e34af1c4e 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -15,7 +15,7 @@ def execute
           # Create a cross-reference note if this Note contains GFM that names an
           # issue, merge request, or commit.
           note.references.each do |mentioned|
-            Note.create_cross_reference_note(mentioned, note.noteable, note.author, note.project)
+            Note.create_cross_reference_note(mentioned, note.noteable, note.author)
           end
 
           execute_hooks(note)
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
index 63431b82471ee5e16a9b1d1283dccc85d6ac957a..45a0db761ec4b5783394141f3892658e35d72ebc 100644
--- a/app/services/notes/update_service.rb
+++ b/app/services/notes/update_service.rb
@@ -13,8 +13,7 @@ def execute
           # Create a cross-reference note if this Note contains GFM that
           # names an issue, merge request, or commit.
           note.references.each do |mentioned|
-            Note.create_cross_reference_note(mentioned, note.noteable,
-                                             note.author, note.project)
+            Note.create_cross_reference_note(mentioned, note.noteable, note.author)
           end
         end
       end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index c7e45a2c2c705ab1addf21ef552c16f4d3f9405f..0d7ffbeebd9a68b045b77a8b62d6893fd51a266a 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -129,9 +129,7 @@ def new_note(note)
 
     # Add all users participating in the thread (author, assignee, comment authors)
     participants = 
-      if target.is_a?(Commit)
-        target.participants(note.project, note.author)
-      elsif target.respond_to?(:participants)
+      if target.respond_to?(:participants)
         target.participants(note.author)
       else
         note.mentioned_users
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index d798d3c3093eda1f4c6233405136dfc31cb91be5..66d61a5ef5b11462c635301dd510d9e01286efb1 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -5,6 +5,8 @@ def initialize(user, params)
     end
 
     def execute
+      forked_from_project_id = params.delete(:forked_from_project_id)
+
       @project = Project.new(params)
 
       # Make sure that the user is allowed to use the specified visibility
@@ -45,10 +47,14 @@ def execute
 
       @project.creator = current_user
 
+      if forked_from_project_id
+        @project.build_forked_project_link(forked_from_project_id: forked_from_project_id)
+      end
+
       Project.transaction do
         @project.save
 
-        unless @project.import?
+        if @project.persisted? && !@project.import?
           unless @project.create_repository
             raise 'Failed to create repository'
           end
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 1e4deb6ed39babde42725d703f7206fd2c14011a..50f208b11d15c396c3ecb1f253e6e28d7e52245f 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -1,66 +1,28 @@
 module Projects
   class ForkService < BaseService
-    include Gitlab::ShellAdapter
-
     def execute
-      @from_project = @project
-
-      project_params = {
-        visibility_level: @from_project.visibility_level,
-        description: @from_project.description,
+      new_params = {
+        forked_from_project_id: @project.id,
+        visibility_level:       @project.visibility_level,
+        description:            @project.description,
+        name:                   @project.name,
+        path:                   @project.path,
+        namespace_id:           @params[:namespace].try(:id) || current_user.namespace.id
       }
 
-      project = Project.new(project_params)
-      project.name = @from_project.name
-      project.path = @from_project.path
-      project.creator = @current_user
-      if @from_project.avatar.present? && @from_project.avatar.image?
-        project.avatar = @from_project.avatar
-      end
-
-      if namespace = @params[:namespace]
-        project.namespace = namespace
-      else
-        project.namespace = @current_user.namespace
+      if @project.avatar.present? && @project.avatar.image?
+        new_params[:avatar] = @project.avatar
       end
 
-      unless @current_user.can?(:create_projects, project.namespace)
-        project.errors.add(:namespace, 'insufficient access rights')
-        return project
-      end
-
-      # If the project cannot save, we do not want to trigger the project destroy
-      # as this can have the side effect of deleting a repo attached to an existing
-      # project with the same name and namespace
-      if project.valid?
-        begin
-          Project.transaction do
-            #First save the DB entries as they can be rolled back if the repo fork fails
-            project.build_forked_project_link(forked_to_project_id: project.id, forked_from_project_id: @from_project.id)
-            if project.save
-              project.team << [@current_user, :master, @current_user]
-            end
-
-            #Now fork the repo
-            unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path)
-              raise 'forking failed in gitlab-shell'
-            end
-
-            project.ensure_satellite_exists
-          end
+      new_project = CreateService.new(current_user, new_params).execute
 
-          if @from_project.gitlab_ci?
-            ForkRegistrationWorker.perform_async(@from_project.id, project.id, @current_user.private_token)
-          end
-        rescue => ex
-          project.errors.add(:base, 'Fork transaction failed.')
-          project.destroy
+      if new_project.persisted?
+        if @project.gitlab_ci?
+          ForkRegistrationWorker.perform_async(@project.id, new_project.id, @current_user.private_token)
         end
-      else
-        project.errors.add(:base, 'Invalid fork destination')
       end
 
-      project
+      new_project
     end
   end
 end
diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb
index c2d8f48f6e420ee0b55e3f8671d036a24052e4d2..b91590a1a9019af31e5149c7c64a712846457088 100644
--- a/app/services/projects/participants_service.rb
+++ b/app/services/projects/participants_service.rb
@@ -13,21 +13,19 @@ def execute(note_type, note_id)
     end
 
     def participants_in(type, id)
-      users = 
+      target = 
         case type
         when "Issue"
-          issue = project.issues.find_by_iid(id)
-          issue.participants(current_user) if issue
+          project.issues.find_by_iid(id)
         when "MergeRequest"
-          merge_request = project.merge_requests.find_by_iid(id)
-          merge_request.participants(current_user) if merge_request
+          project.merge_requests.find_by_iid(id)
         when "Commit"
-          commit = project.repository.commit(id)
-          commit.participants(project, current_user) if commit
+          project.commit(id)
         end
+        
+      return [] unless target
 
-      return [] unless users
-      
+      users = target.participants(current_user)
       sorted(users)
     end
 
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index c5d0b08845ba79d1c3131f47d3b8a6ba10e9d0c6..60235b6be2ab1abe4e51058d721e02671860f9b6 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -7,12 +7,12 @@ def execute_hooks_for(model, event)
 
   def execute_hooks(data)
     SystemHook.all.each do |sh|
-      async_execute_hook sh, data
+      async_execute_hook(sh, data, 'system_hooks')
     end
   end
 
-  def async_execute_hook(hook, data)
-    Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data)
+  def async_execute_hook(hook, data, hook_name)
+    Sidekiq::Client.enqueue(SystemHookWorker, hook.id, data, hook_name)
   end
 
   def build_event_data(model, event)
diff --git a/app/services/test_hook_service.rb b/app/services/test_hook_service.rb
index ab661984fc9ca15257c717b6f4a8b2cf30ed19f6..5b447909def4a2579e4aae7ebce12af92eeff5c4 100644
--- a/app/services/test_hook_service.rb
+++ b/app/services/test_hook_service.rb
@@ -1,7 +1,7 @@
 class TestHookService
   def execute(hook, current_user)
     data = Gitlab::PushDataBuilder.build_sample(project(hook), current_user)
-    hook.execute(data)
+    hook.execute(data, 'push_hooks')
   end
 
   private
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index de64e2e522297ef0928beafe83fb817f86ba3ed5..3f41504b9dbb5036652f51c5e72545f7689daccc 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -42,6 +42,14 @@
       = f.label :default_branch_protection, class: 'control-label col-sm-2'
       .col-sm-10
         = f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
+    .form-group
+      = f.label :default_project_visibility, class: 'control-label col-sm-2'
+      .col-sm-10
+        = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: 'Project')
+    .form-group
+      = f.label :default_snippet_visibility, class: 'control-label col-sm-2'
+      .col-sm-10
+        = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: 'Snippet')
     .form-group
       = f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
       .col-sm-10
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index c1fc1602d0a2a4bea19efffb85df499d9836c229..ba49013d834f15aa3a5b9c92c1181a587faf9582 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -1,4 +1,14 @@
-= render "events/event_last_push", event: @last_push
-= render 'shared/event_filter'
+.hidden-xs
+  = render "events/event_last_push", event: @last_push
+
+  - if current_user
+    %ul.nav.nav-pills.event_filter.pull-right
+      %li.pull-right
+        = link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
+          %i.fa.fa-rss
+          Activity Feed
+
+  = render 'shared/event_filter'
+  %hr
 .content_list
 = spinner
diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder
index 72e9e361dc3d4f984b8cae48ff87e34baddf4650..6e88fc9be409c9c0041be659ba640be100fe2bef 100644
--- a/app/views/dashboard/issues.atom.builder
+++ b/app/views/dashboard/issues.atom.builder
@@ -1,9 +1,9 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "#{current_user.name} issues"
-  xml.link    href: issues_dashboard_url(:atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
-  xml.link    href: issues_dashboard_url(private_token: current_user.private_token), rel: "alternate", type: "text/html"
-  xml.id      issues_dashboard_url(private_token: current_user.private_token)
+  xml.link    href: issues_dashboard_url(format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
+  xml.link    href: issues_dashboard_url, rel: "alternate", type: "text/html"
+  xml.id      issues_dashboard_url
   xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
 
   @issues.each do |issue|
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index db19a46cb263dd075e79dbcd6dc1e62e4396d5a7..62cc80a30dc866d706c7468df14a816c63e5cb2b 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,3 +1,7 @@
+= content_for :meta_tags do
+  - if current_user
+    = auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
+
 %h3.page-title
   Issues
 
@@ -6,5 +10,11 @@
 %hr
 
 .append-bottom-20
+  .pull-right
+    - if current_user
+      .hidden-xs.pull-left
+        = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do
+          %i.fa.fa-rss
+
   = render 'shared/issuable_filter'
 = render 'shared/issues'
diff --git a/app/views/dashboard/show.atom.builder b/app/views/dashboard/show.atom.builder
index da631ecb33e00f01a6f08002da08d78b8f47464c..71edb73cd8a2d93221f44ded6bef637d9e48963c 100644
--- a/app/views/dashboard/show.atom.builder
+++ b/app/views/dashboard/show.atom.builder
@@ -1,9 +1,9 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
-  xml.title   "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}"
-  xml.link    href: dashboard_url(:atom), rel: "self", type: "application/atom+xml"
+  xml.title   "Activity"
+  xml.link    href: dashboard_url(format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
   xml.link    href: dashboard_url, rel: "alternate", type: "text/html"
-  xml.id      projects_url
+  xml.id      dashboard_url
   xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
 
   @events.each do |event|
diff --git a/app/views/dashboard/show.html.haml b/app/views/dashboard/show.html.haml
index fa8946011b741331df864f866420150af171f20c..2754a4894da89dbb99617ea61a7be2eaa0e775c9 100644
--- a/app/views/dashboard/show.html.haml
+++ b/app/views/dashboard/show.html.haml
@@ -1,3 +1,7 @@
+= content_for :meta_tags do
+  - if current_user
+    = auto_discovery_link_tag(:atom, dashboard_url(format: :atom, private_token: current_user.private_token), title: "All activity")
+
 - if @projects.any?
   .dashboard.row
     %section.activities.col-md-8
diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml
index 552525f4a0783257f881e7dcd125ccdfc986301c..c2577a249824d165272c23eb4d9d3ccf891c89f1 100644
--- a/app/views/events/event/_created_project.html.haml
+++ b/app/views/events/event/_created_project.html.haml
@@ -18,7 +18,7 @@
           %a.twitter-share-button{ |
             href: "https://twitter.com/share", |
             "data-url" => event.project.web_url, |
-            "data-text" => "I just #{event.project.imported? ? "imported" : "created"} a new project in GitLab! GitLab is version control on your server.", |
+            "data-text" => "I just #{event.action_name} a new project on GitLab! GitLab is version control on your server.", |
             "data-size" => "medium", |
             "data-related" => "gitlab", |
             "data-hashtags" => "gitlab", |
diff --git a/app/views/groups/_settings_nav.html.haml b/app/views/groups/_settings_nav.html.haml
index 6c4a7572cece0753187a8164a324a287eef8e780..b660e7e7512bd428feeff16df94510c6837bdcb8 100644
--- a/app/views/groups/_settings_nav.html.haml
+++ b/app/views/groups/_settings_nav.html.haml
@@ -1,12 +1,12 @@
 %ul.sidebar-subnav
   = nav_link(path: 'groups#edit') do
-    = link_to edit_group_path(@group), title: 'Group' do
-      %i.fa.fa-pencil-square-o
+    = link_to edit_group_path(@group), title: 'Group', data: {placement: 'right'} do
+      = icon('pencil-square-o')
       %span
         Group
   = nav_link(path: 'groups#projects') do
-    = link_to projects_group_path(@group), title: 'Projects' do
-      %i.fa.fa-folder
+    = link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do
+      = icon('folder')
       %span
         Projects
   - if ldap_enabled?
diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder
index 240001967f36a8e55664e6c7d99da2c95634f2db..66fe7e25871ab3c55b4c730b5258b8a8015421b6 100644
--- a/app/views/groups/issues.atom.builder
+++ b/app/views/groups/issues.atom.builder
@@ -1,9 +1,9 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "#{@user.name} issues"
-  xml.link    :href => issues_dashboard_url(:atom, :private_token => @user.private_token), :rel => "self", :type => "application/atom+xml"
-  xml.link    :href => issues_dashboard_url(:private_token => @user.private_token), :rel => "alternate", :type => "text/html"
-  xml.id      issues_dashboard_url(:private_token => @user.private_token)
+  xml.link    href: issues_dashboard_url(format: :atom, private_token: @user.private_token), rel: "self", type: "application/atom+xml"
+  xml.link    href: issues_dashboard_url, rel: "alternate", type: "text/html"
+  xml.id      issues_dashboard_url
   xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
 
   @issues.each do |issue|
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 6c0d89c4e7cbf7e67aecb1fd8333a7526c50de86..cf0da2da46696b37a2f34f9c0750c94a69bb5d15 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,3 +1,7 @@
+= content_for :meta_tags do
+  - if current_user
+    = auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
+
 %h3.page-title
   Issues
 
@@ -10,5 +14,11 @@
 %hr
 
 .append-bottom-20
+  .pull-right
+    - if current_user
+      .hidden-xs.pull-left
+        = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do
+          %i.fa.fa-rss
+
   = render 'shared/issuable_filter'
 = render 'shared/issues'
diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder
index c78bd1bd2637f2412f6f1f90a346ea86fe495e57..b52e78faaa3037da5d5c7fd5bca9eb415ac8a8d7 100644
--- a/app/views/groups/show.atom.builder
+++ b/app/views/groups/show.atom.builder
@@ -1,9 +1,9 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
-  xml.title   "Group feed - #{@group.name}"
-  xml.link    href: group_path(@group, :atom), rel: "self", type: "application/atom+xml"
-  xml.link    href: group_path(@group), rel: "alternate", type: "text/html"
-  xml.id      projects_url
+  xml.title   "#{@group.name} activity"
+  xml.link    href: group_url(@group, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
+  xml.link    href: group_url(@group), rel: "alternate", type: "text/html"
+  xml.id      group_url(@group)
   xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
 
   @events.each do |event|
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index a4660851da9c5b91473e0fcd4d837cbd46d2e435..872a498463e851172c058826b3357b00e0b17428 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,3 +1,7 @@
+= content_for :meta_tags do
+  - if current_user
+    = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
+
 .dashboard
   .header-with-avatar.clearfix
     = image_tag group_icon(@group), class: "avatar group-avatar s90"
@@ -11,9 +15,20 @@
   %hr
   .row
     %section.activities.col-md-8
-      - if current_user
-        = render "events/event_last_push", event: @last_push
-        = render 'shared/event_filter'
+      .hidden-xs
+        - if current_user
+          = render "events/event_last_push", event: @last_push
+
+          - if current_user
+            %ul.nav.nav-pills.event_filter.pull-right
+              %li
+                = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
+                  %i.fa.fa-rss
+                  Activity Feed
+
+          = render 'shared/event_filter'
+          %hr
+
       .content_list
       = spinner
     %aside.side.col-md-4
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 03830e7ec781330284ff6402a9298415e66eec87..fd4165d6b642ef426041816cc41cb2d0c0a50bd1 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -5,6 +5,7 @@
   %title
     = "#{title} | " if defined?(title)
     GitLab
+
   = favicon_link_tag 'favicon.ico'
   = stylesheet_link_tag    "application", :media => "all"
   = stylesheet_link_tag    "print", :media => "print"
@@ -14,16 +15,8 @@
   %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'}
   %meta{name: 'theme-color', content: '#474D57'}
 
+  = yield(:meta_tags)
+
   = render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
   = render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
   = render 'layouts/bootlint' if Rails.env.development?
-
-  -# Atom feed
-  - if current_user
-    - if controller_name == 'projects' && action_name == 'index'
-      = auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed"
-    - if @project && !@project.new_record?
-      - if current_controller?(:tree, :commits)
-        = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}")
-      - if current_controller?(:issues)
-        = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
index 281ce31fe9c1ed9d5a6f9676c289201435c6e1b7..8d4c7b39b19b0c0656d22aaf4bb37c01c8b07ccd 100644
--- a/app/views/layouts/_head_panel.html.haml
+++ b/app/views/layouts/_head_panel.html.haml
@@ -2,48 +2,47 @@
   .navbar-inner
     .container
       %div.app_logo
-        = link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do
+        = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do
           = brand_header_logo
           %h3 GitLab
       %h1.title= title
 
-      %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
+      %button.navbar-toggle{type: 'button', data: {target: '.navbar-collapse', toggle: 'collapse'}}
         %span.sr-only Toggle navigation
-        %i.fa.fa-bars
+        = icon('bars')
 
       .navbar-collapse.collapse
         %ul.nav.navbar-nav
           %li.hidden-sm.hidden-xs
-            = render "layouts/search"
+            = render 'layouts/search'
           %li.visible-sm.visible-xs
-            = link_to search_path, title: "Search", class: 'has_bottom_tooltip', 'data-original-title' => 'Search area' do
-              %i.fa.fa-search
+            = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
+              = icon('search')
           %li
-            = link_to help_path, title: 'Help', class: 'has_bottom_tooltip',
-               'data-original-title' => 'Help'  do
-              %i.fa.fa-question-circle
+            = link_to help_path, title: 'Help', data: {toggle: 'tooltip', placement: 'bottom'} do
+              = icon('question-circle')
           %li
-            = link_to explore_root_path, title: "Explore", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
-              %i.fa.fa-globe
+            = link_to explore_root_path, title: 'Explore', data: {toggle: 'tooltip', placement: 'bottom'} do
+              = icon('globe')
           %li
-            = link_to user_snippets_path(current_user), title: "Your snippets", class: 'has_bottom_tooltip', 'data-original-title' => 'Your snippets' do
-              %i.fa.fa-clipboard
+            = link_to user_snippets_path(current_user), title: 'Your snippets', data: {toggle: 'tooltip', placement: 'bottom'} do
+              = icon('clipboard')
           - if current_user.is_admin?
             %li
-              = link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do
-                %i.fa.fa-cogs
+              = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do
+                = icon('cogs')
           - if current_user.can_create_project?
             %li
-              = link_to new_project_path, title: "New project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project'  do
-                %i.fa.fa-plus
+              = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do
+                = icon('plus')
           %li
-            = link_to profile_path, title: "Profile settings", class: 'has_bottom_tooltip', 'data-original-title' => 'Profile settings"'  do
-              %i.fa.fa-user
+            = link_to profile_path, title: 'Profile settings', data: {toggle: 'tooltip', placement: 'bottom'} do
+              = icon('user')
           %li
-            = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Sign out", class: 'has_bottom_tooltip', 'data-original-title' => 'Sign out'  do
-              %i.fa.fa-sign-out
+            = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do
+              = icon('sign-out')
           %li.hidden-xs
-            = link_to current_user, class: "profile-pic has_bottom_tooltip", id: 'profile-pic', 'data-original-title' => 'Your profile' do
+            = link_to current_user, class: 'profile-pic', id: 'profile-pic', data: {toggle: 'tooltip', placement: 'bottom'} do
               = image_tag avatar_icon(current_user.email, 60), alt: 'User activity'
 
 = render 'shared/outdated_browser'
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 1c164800b0e686d335484fcafa254620d8f436b1..0fa2ec9824dc44f2be60197f0d1d008d4810a217 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -15,7 +15,3 @@
           = yield
 
 = yield :embedded_scripts
-
-:coffeescript
-  $('.page-sidebar-collapsed .nav-sidebar a').tooltip placement: "right"
-
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index dc90d29c66ea65fd60b547898dc8427ca79b7098..fda6880163d8cf3d7ce7cac49fb07f7aa784b617 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -5,37 +5,37 @@
       %span
         Overview
   = nav_link(controller: :projects) do
-    = link_to admin_namespaces_projects_path, title: 'Projects' do
+    = link_to admin_namespaces_projects_path, title: 'Projects', data: {placement: 'right'} do
       = icon('cube fw')
       %span
         Projects
   = nav_link(controller: :users) do
-    = link_to admin_users_path, title: 'Users' do
+    = link_to admin_users_path, title: 'Users', data: {placement: 'right'} do
       = icon('user fw')
       %span
         Users
   = nav_link(controller: :groups) do
-    = link_to admin_groups_path, title: 'Groups' do
+    = link_to admin_groups_path, title: 'Groups', data: {placement: 'right'} do
       = icon('group fw')
       %span
         Groups
   = nav_link(controller: :deploy_keys) do
-    = link_to admin_deploy_keys_path, title: 'Deploy Keys' do
+    = link_to admin_deploy_keys_path, title: 'Deploy Keys', data: {placement: 'right'} do
       = icon('key fw')
       %span
         Deploy Keys
   = nav_link(controller: :logs) do
-    = link_to admin_logs_path, title: 'Logs' do
+    = link_to admin_logs_path, title: 'Logs', data: {placement: 'right'} do
       = icon('file-text fw')
       %span
         Logs
   = nav_link(controller: :broadcast_messages) do
-    = link_to admin_broadcast_messages_path, title: 'Broadcast Messages' do
+    = link_to admin_broadcast_messages_path, title: 'Broadcast Messages', data: {placement: 'right'} do
       = icon('bullhorn fw')
       %span
         Messages
   = nav_link(controller: :hooks) do
-    = link_to admin_hooks_path, title: 'Hooks' do
+    = link_to admin_hooks_path, title: 'Hooks', data: {placement: 'right'} do
       = icon('external-link fw')
       %span
         Hooks
@@ -45,7 +45,7 @@
       %span
         Git Hooks
   = nav_link(controller: :background_jobs) do
-    = link_to admin_background_jobs_path, title: 'Background Jobs' do
+    = link_to admin_background_jobs_path, title: 'Background Jobs', data: {placement: 'right'} do
       = icon('cog fw')
       %span
         Background Jobs
@@ -56,19 +56,19 @@
         Appearance
 
   = nav_link(controller: :applications) do
-    = link_to admin_applications_path, title: 'Applications' do
+    = link_to admin_applications_path, title: 'Applications', data: {placement: 'right'} do
       = icon('cloud fw')
       %span
         Applications
 
   = nav_link(controller: :services) do
-    = link_to admin_application_settings_services_path, title: 'Service Templates' do
+    = link_to admin_application_settings_services_path, title: 'Service Templates', data: {placement: 'right'} do
       = icon('copy fw')
       %span
         Service Templates
 
   = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
-    = link_to admin_application_settings_path, title: 'Settings' do
+    = link_to admin_application_settings_path, title: 'Settings', data: {placement: 'right'} do
       = icon('cogs fw')
       %span
         Settings
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index c5997e44370bcdbaa2a01b038dcf756812261dc9..d46dba4a240544f0f38876643eab2b10fa5b3a36 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,38 +1,38 @@
 %ul.nav.nav-sidebar
   = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
-    = link_to root_path, title: 'Home', class: 'shortcuts-activity' do
+    = link_to root_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
       = icon('dashboard fw')
       %span
         Your Projects
   = nav_link(path: 'projects#starred') do
-    = link_to starred_dashboard_projects_path, title: 'Starred Projects' do
+    = link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
       = icon('star fw')
       %span
         Starred Projects
   = nav_link(controller: :groups) do
-    = link_to dashboard_groups_path, title: 'Groups' do
+    = link_to dashboard_groups_path, title: 'Groups', data: {placement: 'right'} do
       = icon('group fw')
       %span
         Groups
   = nav_link(controller: :milestones) do
-    = link_to dashboard_milestones_path, title: 'Milestones' do
+    = link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do
       = icon('clock-o fw')
       %span
         Milestones
   = nav_link(path: 'dashboard#issues') do
-    = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do
+    = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
       = icon('exclamation-circle fw')
       %span
         Issues
         %span.count= current_user.assigned_issues.opened.count
   = nav_link(path: 'dashboard#merge_requests') do
-    = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do
+    = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
       = icon('tasks fw')
       %span
         Merge Requests
         %span.count= current_user.assigned_merge_requests.opened.count
   = nav_link(controller: :help) do
-    = link_to help_path, title: 'Help' do
+    = link_to help_path, title: 'Help', data: {placement: 'right'} do
       = icon('question-circle fw')
       %span
         Help
diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml
index 38d84cd9b51f00dfd22a70b1339b56e4996c0c2d..66870e84ceb1470f97b960e440542c60a8cd472c 100644
--- a/app/views/layouts/nav/_explore.html.haml
+++ b/app/views/layouts/nav/_explore.html.haml
@@ -1,18 +1,18 @@
 %ul.nav.nav-sidebar
   = nav_link(path: 'projects#trending') do
-    = link_to explore_root_path do
+    = link_to explore_root_path, title: 'Trending Projects', data: {placement: 'right'} do
       = icon('comments fw')
       %span Trending Projects
   = nav_link(path: 'projects#starred') do
-    = link_to starred_explore_projects_path do
+    = link_to starred_explore_projects_path, title: 'Most-starred Projects', data: {placement: 'right'} do
       = icon('star fw')
-      %span Most Starred Projects
+      %span Most-starred Projects
   = nav_link(path: 'projects#index') do
-    = link_to explore_projects_path do
+    = link_to explore_projects_path, title: 'All Projects', data: {placement: 'right'} do
       = icon('bookmark fw')
       %span All Projects
   = nav_link(controller: :groups) do
-    = link_to explore_groups_path do
+    = link_to explore_groups_path, title: 'All Groups', data: {placement: 'right'} do
       = icon('group fw')
       %span All Groups
 
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 7cce9ffe3d516e13c58bac0885f136847e54cfad..74a8526dbd7f10a62c7729755fb94a618936f737 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,38 +1,38 @@
 %ul.nav.nav-sidebar
   = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
-    = link_to group_path(@group), title: "Home" do
+    = link_to group_path(@group), title: 'Home', data: {placement: 'right'} do
       = icon('dashboard fw')
       %span
         Activity
   - if current_user
     = nav_link(controller: [:group, :milestones]) do
-      = link_to group_milestones_path(@group), title: 'Milestones' do
+      = link_to group_milestones_path(@group), title: 'Milestones', data: {placement: 'right'} do
         = icon('clock-o fw')
         %span
           Milestones
   = nav_link(path: 'groups#issues') do
-    = link_to issues_group_path(@group), title: 'Issues' do
+    = link_to issues_group_path(@group), title: 'Issues', data: {placement: 'right'} do
       = icon('exclamation-circle fw')
       %span
         Issues
         - if current_user
           %span.count= Issue.opened.of_group(@group).count
   = nav_link(path: 'groups#merge_requests') do
-    = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
+    = link_to merge_requests_group_path(@group), title: 'Merge Requests', data: {placement: 'right'} do
       = icon('tasks fw')
       %span
         Merge Requests
         - if current_user
           %span.count= MergeRequest.opened.of_group(@group).count
   = nav_link(controller: [:group_members]) do
-    = link_to group_group_members_path(@group), title: 'Members' do
+    = link_to group_group_members_path(@group), title: 'Members', data: {placement: 'right'} do
       = icon('users fw')
       %span
         Members
 
   - if can?(current_user, :admin_group, @group)
     = nav_link(html_options: { class: "#{"active" if group_settings_page?} separate-item" }) do
-      = link_to edit_group_path(@group), title: 'Settings', class: "tab no-highlight" do
+      = link_to edit_group_path(@group), title: 'Settings', class: 'tab no-highlight', data: {placement: 'right'} do
         = icon ('cogs fw')
         %span
           Settings
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 9f2932c79cd0e53b0c4753143feddd5d7b9e3040..31d8ed3ed8639b1b065f0033d0606cc14649363b 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,50 +1,50 @@
 %ul.nav.nav-sidebar
   = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
-    = link_to profile_path, title: "Profile" do
+    = link_to profile_path, title: 'Profile', data: {placement: 'right'} do
       = icon('user fw')
       %span
         Profile
   = nav_link(controller: :accounts) do
-    = link_to profile_account_path, title: 'Account' do
+    = link_to profile_account_path, title: 'Account', data: {placement: 'right'} do
       = icon('gear fw')
       %span
         Account
   = nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do
-    = link_to applications_profile_path, title: 'Applications' do
+    = link_to applications_profile_path, title: 'Applications', data: {placement: 'right'} do
       = icon('cloud fw')
       %span
         Applications
   = nav_link(controller: :emails) do
-    = link_to profile_emails_path, title: 'Emails' do
+    = link_to profile_emails_path, title: 'Emails', data: {placement: 'right'} do
       = icon('envelope-o fw')
       %span
         Emails
         %span.count= current_user.emails.count + 1
   - unless current_user.ldap_user?
     = nav_link(controller: :passwords) do
-      = link_to edit_profile_password_path, title: 'Password' do
+      = link_to edit_profile_password_path, title: 'Password', data: {placement: 'right'} do
         = icon('lock fw')
         %span
           Password
   = nav_link(controller: :notifications) do
-    = link_to profile_notifications_path, title: 'Notifications' do
+    = link_to profile_notifications_path, title: 'Notifications', data: {placement: 'right'} do
       = icon('inbox fw')
       %span
         Notifications
 
   = nav_link(controller: :keys) do
-    = link_to profile_keys_path, title: 'SSH Keys' do
+    = link_to profile_keys_path, title: 'SSH Keys', data: {placement: 'right'} do
       = icon('key fw')
       %span
         SSH Keys
         %span.count= current_user.keys.count
   = nav_link(path: 'profiles#design') do
-    = link_to design_profile_path, title: 'Design' do
+    = link_to design_profile_path, title: 'Design', data: {placement: 'right'} do
       = icon('image fw')
       %span
         Design
   = nav_link(path: 'profiles#history') do
-    = link_to history_profile_path, title: 'History' do
+    = link_to history_profile_path, title: 'History', data: {placement: 'right'} do
       = icon('history fw')
       %span
         History
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index a2a9d8a340b96d6fbef47e21dfddbb71ebb2421b..01b3d70194fcb8225dfc969bf1c718099ba549a5 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,7 +1,7 @@
 %ul.project-navigation.nav.nav-sidebar
   - if @project_settings_nav
     = nav_link do
-      = link_to project_path(@project), title: 'Back to project', class: "" do
+      = link_to project_path(@project), title: 'Back to project', data: {placement: 'right'} do
         = icon('caret-square-o-left fw')
         %span
           Back to project
@@ -11,49 +11,49 @@
     = render 'projects/settings_nav'
 
   - else
-    = nav_link(path: 'projects#show', html_options: {class: "home"}) do
-      = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
+    = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
+      = link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
         = icon('dashboard fw')
         %span
           Project
     - if project_nav_tab? :files
       = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
-        = link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files',  class: 'shortcuts-tree' do
+        = link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files',  class: 'shortcuts-tree', data: {placement: 'right'} do
           = icon('files-o fw')
           %span
             Files
 
     - if project_nav_tab? :commits
       = nav_link(controller: %w(commit commits compare repositories tags branches)) do
-        = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits' do
+        = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
           = icon('history fw')
           %span
             Commits
 
     - if project_nav_tab? :network
       = nav_link(controller: %w(network)) do
-        = link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network' do
+        = link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
           = icon('code-fork fw')
           %span
             Network
 
     - if project_nav_tab? :graphs
       = nav_link(controller: %w(graphs)) do
-        = link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs',  class: 'shortcuts-graphs' do
+        = link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs',  class: 'shortcuts-graphs', data: {placement: 'right'} do
           = icon('area-chart fw')
           %span
             Graphs
 
     - if project_nav_tab? :milestones
       = nav_link(controller: :milestones) do
-        = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
+        = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do
           = icon('clock-o fw')
           %span
             Milestones
 
     - if project_nav_tab? :issues
       = nav_link(controller: :issues) do
-        = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do
+        = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
           = icon('exclamation-circle fw')
           %span
             Issues
@@ -62,7 +62,7 @@
 
     - if project_nav_tab? :merge_requests
       = nav_link(controller: :merge_requests) do
-        = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests'  do
+        = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
           = icon('tasks fw')
           %span
             Merge Requests
@@ -70,28 +70,28 @@
 
     - if project_nav_tab? :labels
       = nav_link(controller: :labels) do
-        = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
+        = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
           = icon('tags fw')
           %span
             Labels
 
     - if project_nav_tab? :wiki
       = nav_link(controller: :wikis) do
-        = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
+        = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do
           = icon('book fw')
           %span
             Wiki
 
     - if project_nav_tab? :snippets
       = nav_link(controller: :snippets) do
-        = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
+        = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
           = icon('file-text-o fw')
           %span
             Snippets
 
     - if project_nav_tab? :settings
       = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
-        = link_to edit_project_path(@project), title: 'Settings', class: "stat-tab tab no-highlight" do
+        = link_to edit_project_path(@project), title: 'Settings', class: 'stat-tab tab no-highlight', data: {placement: 'right'} do
           = icon('cogs fw')
           %span
             Settings
diff --git a/app/views/layouts/nav/_snippets.html.haml b/app/views/layouts/nav/_snippets.html.haml
index edd05f2dd818fed95847fb108956c78caa92221e..0de3a9e5bb77ac87b798cbd293c5c88fbf33a27b 100644
--- a/app/views/layouts/nav/_snippets.html.haml
+++ b/app/views/layouts/nav/_snippets.html.haml
@@ -1,11 +1,11 @@
 %ul.nav.nav-sidebar
   = nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do
-    = link_to user_snippets_path(current_user), title: 'Your snippets' do
+    = link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
       = icon('dashboard fw')
       %span
         Your Snippets
   = nav_link(path: snippets_path) do
-    = link_to snippets_path, title: 'Discover snippets' do
+    = link_to snippets_path, title: 'Discover snippets', data: {placement: 'right'} do
       = icon('globe fw')
       %span
         Discover Snippets
diff --git a/app/views/profiles/update.js.erb b/app/views/profiles/update.js.erb
index e664ac2a52ab81cd0cb1f128664ff86909bb8ab3..db37619136d32a0ce7585e40a0ef52ab747dc14c 100644
--- a/app/views/profiles/update.js.erb
+++ b/app/views/profiles/update.js.erb
@@ -1,9 +1,3 @@
 // Remove body class for any previous theme, re-add current one
-$('body').removeClass('ui_basic ui_mars ui_modern ui_gray ui_color light_theme dark_theme')
+$('body').removeClass('<%= Gitlab::Theme.body_classes %>')
 $('body').addClass('<%= app_theme %> <%= theme_type %>')
-
-// Re-render the header to reflect the new theme
-$('header').html('<%= escape_javascript(render("layouts/head_panel", title: "Profile")) %>')
-
-// Re-initialize header tooltips
-$('.has_bottom_tooltip').tooltip({placement: 'bottom'})
diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml
index b45af073a7d415eebd2a6eaac2fc72ac5508f067..299c8ad713dc6ada0dd390aa7ac9770a06b14fc9 100644
--- a/app/views/projects/_settings_nav.html.haml
+++ b/app/views/projects/_settings_nav.html.haml
@@ -1,12 +1,12 @@
 %ul.project-settings-nav.sidebar-subnav
   = nav_link(path: 'projects#edit') do
-    = link_to edit_project_path(@project), title: 'Project', class: "stat-tab tab " do
-      %i.fa.fa-pencil-square-o
+    = link_to edit_project_path(@project), title: 'Project', class: 'stat-tab tab', data: {placement: 'right'} do
+      = icon('pencil-square-o')
       %span
         Project
   = nav_link(controller: [:project_members, :teams]) do
-    = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: "team-tab tab" do
-      %i.fa.fa-users
+    = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
+      = icon('users')
       %span
         Members
   = nav_link(controller: :group_links) do
@@ -15,13 +15,13 @@
       %span
         Groups
   = nav_link(controller: :deploy_keys) do
-    = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
-      %i.fa.fa-key
+    = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do
+      = icon('key')
       %span
         Deploy Keys
   = nav_link(controller: :hooks) do
-    = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks' do
-      %i.fa.fa-link
+    = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do
+      = icon('link')
       %span
         Web Hooks
   = nav_link(controller: :git_hooks) do
@@ -30,13 +30,13 @@
       %span
         Git Hooks
   = nav_link(controller: :services) do
-    = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
-      %i.fa.fa-cogs
+    = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do
+      = icon('cogs')
       %span
         Services
   = nav_link(controller: :protected_branches) do
-    = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
-      %i.fa.fa-lock
+    = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do
+      = icon('lock')
       %span
         Protected branches
   = nav_link(controller: :audit_events) do
diff --git a/app/views/projects/_visibility_level.html.haml b/app/views/projects/_visibility_level.html.haml
deleted file mode 100644
index 42c8e685224c022894f724ce9140d50d6be4273e..0000000000000000000000000000000000000000
--- a/app/views/projects/_visibility_level.html.haml
+++ /dev/null
@@ -1,27 +0,0 @@
-.form-group.project-visibility-level-holder
-  = f.label :visibility_level, class: 'control-label' do
-    Visibility Level
-    = link_to "(?)", help_page_path("public_access", "public_access")
-  .col-sm-10
-    - if can_change_visibility_level
-      - Gitlab::VisibilityLevel.values.each do |level|
-        .radio
-          - restricted = restricted_visibility_levels.include?(level)
-          = label :project_visibility_level, level do
-            = f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
-            = visibility_level_icon(level)
-            .option-title
-              = visibility_level_label(level)
-            .option-descr
-              = visibility_level_description(level)
-      - unless restricted_visibility_levels.empty?
-        .col-sm-10
-          %span.info
-            Some visibility level settings have been restricted by the administrator.
-    - else
-      .col-sm-10
-        %span.info
-          = visibility_level_icon(visibility_level)
-          %strong
-            = visibility_level_label(visibility_level)
-          .light= visibility_level_description(visibility_level)
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index e6a859fea8fbe96445adf1327777199c9c570c1f..89dd68d647185a0da034e66b57b89f5d389a0168 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -12,7 +12,7 @@
     .file-content.blame.highlight
       %table
         - @blame.each do |commit, lines, since|
-          - commit = Commit.new(commit)
+          - commit = Commit.new(commit, @project)
           %tr
             %td.blame-commit
               %span.commit
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 8e1aaa4d051c0da760105c4f819825ac0f4b6a0b..083fca9b6582fbeeff93ec3f9060e3dca1b678fe 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -12,7 +12,7 @@
       - if @note_counts
         - note_count = @note_counts.fetch(commit.id, 0)
       - else
-        - notes = commit.notes(project)
+        - notes = commit.notes
         - note_count = notes.user.count
 
       - if note_count > 0
diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml
index 2ee7d73bd20950eb79eca087882a905d5b1e2540..ce60fbdf032128249361b358b92fa6119491abc9 100644
--- a/app/views/projects/commits/_commit_list.html.haml
+++ b/app/views/projects/commits/_commit_list.html.haml
@@ -3,9 +3,9 @@
     Commits (#{@commits.count})
   - if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
     %ul.well-list
-      - Commit.decorate(@commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE)).each do |commit|
+      - Commit.decorate(@commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE), @project).each do |commit|
         = render "projects/commits/inline_commit", commit: commit, project: @project
       %li.warning-row.unstyled
         other #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} commits hidden to prevent performance issues.
   - else
-    %ul.well-list= render Commit.decorate(@commits), project: @project
+    %ul.well-list= render Commit.decorate(@commits, @project), project: @project
diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder
index 9211de72b1b49bad03734dc430e09337fdf488f5..01edd9447ce2a9ef1da2e8288efb876d7a3dbb85 100644
--- a/app/views/projects/commits/show.atom.builder
+++ b/app/views/projects/commits/show.atom.builder
@@ -1,18 +1,18 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
-  xml.title   "Recent commits to #{@project.name}:#{@ref}"
-  xml.link    :href => namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom), :rel => "self", :type => "application/atom+xml"
-  xml.link    :href => namespace_project_commits_url(@project.namespace, @project, @ref), :rel => "alternate", :type => "text/html"
+  xml.title   "#{@project.name}:#{@ref} commits"
+  xml.link    href: namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
+  xml.link    href: namespace_project_commits_url(@project.namespace, @project, @ref), rel: "alternate", type: "text/html"
   xml.id      namespace_project_commits_url(@project.namespace, @project, @ref)
   xml.updated @commits.first.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") if @commits.any?
 
   @commits.each do |commit|
     xml.entry do
-      xml.id      namespace_project_commit_url(@project.namespace, @project, :id => commit.id)
-      xml.link    :href => namespace_project_commit_url(@project.namespace, @project, :id => commit.id)
-      xml.title   truncate(commit.title, :length => 80)
+      xml.id      namespace_project_commit_url(@project.namespace, @project, id: commit.id)
+      xml.link    href: namespace_project_commit_url(@project.namespace, @project, id: commit.id)
+      xml.title   truncate(commit.title, length: 80)
       xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")
-      xml.media   :thumbnail, :width => "40", :height => "40", :url => avatar_icon(commit.author_email)
+      xml.media   :thumbnail, width: "40", height: "40", url: avatar_icon(commit.author_email)
       xml.author do |author|
         xml.name commit.author_name
         xml.email commit.author_email
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index 7ea855e1a4e7b863c1c14f6ba2735cf8d17f1fa0..fb1012deb74a69c0d5bc1c4084a29e921af5fa78 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -1,3 +1,7 @@
+= content_for :meta_tags do
+  - if current_user
+    = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
+
 = render "head"
 
 .tree-ref-holder
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index f9437c89dacb33766ea3302f06c9154f1729fb83..168dd53e169a5ea413840eaf097c7b4a2ce028d9 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -29,7 +29,7 @@
                 .col-sm-10= f.select(:default_branch, @repository.branch_names, {}, {class: 'select2 select-wide'})
 
 
-          = render "visibility_level", f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project)
+          = render 'shared/visibility_level', f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project), form_model: @project
 
           .form-group
             = f.label :tag_list, "Tags", class: 'control-label'
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 998e74d12cf8539b7fd7a5e9597da79de5c04cb5..ef36d1f95470c1a8e0710d3e7b540abaca17f58b 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -6,24 +6,34 @@
   .issue-title
     %span.str-truncated
       = link_to_gfm issue.title, issue_path(issue), class: "row_title"
+    .issue-labels
+      - issue.labels.each do |label|
+        = link_to namespace_project_issues_path(issue.project.namespace, issue.project, label_name: label.name) do
+          = render_colored_label(label)
     .pull-right.light
       - if issue.closed?
         %span
           CLOSED
+      - if issue.assignee
+        = link_to_member(@project, issue.assignee, name: false)
       - note_count = issue.notes.user.count
       - if note_count > 0
         &nbsp;
         %span
           %i.fa.fa-comments
           = note_count
+      - else
+        &nbsp;
+        %span.issue-no-comments
+          %i.fa.fa-comments
+          = 0
 
   .issue-info
-    = link_to "##{issue.iid}", issue_path(issue), class: "light"
-    - if issue.assignee
-      assigned to #{link_to_member(@project, issue.assignee)}
+    = "##{issue.iid} opened #{time_ago_with_tooltip(issue.created_at, 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
     - if issue.votes_count > 0
       = render 'votes/votes_inline', votable: issue
     - if issue.milestone
+      &nbsp;
       %span
         %i.fa.fa-clock-o
         = issue.milestone.title
@@ -33,20 +43,3 @@
 
     .pull-right.issue-updated-at
       %small updated #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_update_ago')}
-
-    .issue-labels
-      - issue.labels.each do |label|
-        = link_to namespace_project_issues_path(issue.project.namespace, issue.project, label_name: label.name) do
-          = render_colored_label(label)
-
-  .issue-actions
-    - if can? current_user, :modify_issue, issue
-      - if issue.closed?
-        = link_to 'Reopen', issue_path(issue, issue: {state_event: :reopen }, status_only: true), method: :put,  class: "btn btn-sm btn-grouped reopen_issue btn-reopen", remote: true
-      - else
-        = link_to 'Close', issue_path(issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-sm btn-grouped close_issue btn-close", remote: true
-      = link_to edit_namespace_project_issue_path(issue.project.namespace, issue.project, issue), class: "btn btn-sm edit-issue-link btn-grouped" do
-        %i.fa.fa-pencil-square-o
-        Edit
-
-
diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder
index 126f2c07faaa68192fc4b4a09dc8586eced6c497..5fa8fbdf8935629c0732b5b95ad6216cc4dd3421 100644
--- a/app/views/projects/issues/index.atom.builder
+++ b/app/views/projects/issues/index.atom.builder
@@ -1,8 +1,8 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "#{@project.name} issues"
-  xml.link    :href => namespace_project_issues_url(@project.namespace, @project, :atom), :rel => "self", :type => "application/atom+xml"
-  xml.link    :href => namespace_project_issues_url(@project.namespace, @project), :rel => "alternate", :type => "text/html"
+  xml.link    href: namespace_project_issues_url(@project.namespace, @project, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
+  xml.link    href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html"
   xml.id      namespace_project_issues_url(@project.namespace, @project)
   xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
 
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index d3c7ae24a752f4b3298e9951c94de9bf67511b9f..c2522816f3b1f808dbb02338a2767b6507b9bca9 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -1,3 +1,7 @@
+= content_for :meta_tags do
+  - if current_user
+    = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
+
 .append-bottom-10
   .pull-right
     .pull-left
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 4f30d1e69f79fd0525bfb4a290bffc15ad19f011..5d5a23b5409268722665f56d2656c9fbd441dee9 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -2,6 +2,10 @@
   .merge-request-title
     %span.str-truncated
       = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
+    .merge-request-labels
+      - merge_request.labels.each do |label|
+        = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, label_name: label.name) do
+          = render_colored_label(label)
     .pull-right.light
       - if merge_request.merged?
         %span
@@ -17,20 +21,26 @@
             %i.fa.fa-code-fork
             %span= merge_request.target_branch
       - note_count = merge_request.mr_and_commit_notes.user.count
+      - if merge_request.assignee
+        &nbsp;
+        = link_to_member(merge_request.source_project, merge_request.assignee, name: false)
       - if note_count > 0
         &nbsp;
         %span
           %i.fa.fa-comments
           = note_count
+      - else
+        &nbsp;
+        %span.merge-request-no-comments
+          %i.fa.fa-comments
+          = 0
+
   .merge-request-info
-    = link_to "##{merge_request.iid}", merge_request_path(merge_request), class: "light"
-    - if merge_request.assignee
-      assigned to #{link_to_member(merge_request.source_project, merge_request.assignee)}
-    - else
-      Unassigned
+    = "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe
     - if merge_request.votes_count > 0
       = render 'votes/votes_inline', votable: merge_request
     - if merge_request.milestone_id?
+      &nbsp;
       %span
         %i.fa.fa-clock-o
         = merge_request.milestone.title
@@ -38,11 +48,5 @@
       %span.task-status
         = merge_request.task_status
 
-
     .pull-right.hidden-xs
       %small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
-
-    .merge-request-labels
-      - merge_request.labels.each do |label|
-        = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, label_name: label.name) do
-          = render_colored_label(label)
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index a06c85b4251261b4934628cd4bac968224c9d966..47c69f89a97888c6ee3dde97a3977c9e868de584 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -93,7 +93,7 @@
           %span.light (optional)
         .col-sm-10
           = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250, tabindex: 3
-      = render "visibility_level", f: f, visibility_level: gitlab_config.default_projects_features.visibility_level, can_change_visibility_level: true
+      = render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project
 
       .form-actions
         = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder
new file mode 100644
index 0000000000000000000000000000000000000000..bb713dcafa52cc2f4011e9c6742274a0050d1727
--- /dev/null
+++ b/app/views/projects/show.atom.builder
@@ -0,0 +1,12 @@
+xml.instruct!
+xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
+  xml.title   "#{@project.name} activity"
+  xml.link    href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
+  xml.link    href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html"
+  xml.id      namespace_project_url(@project.namespace, @project)
+  xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
+
+  @events.each do |event|
+    event_to_atom(xml, event)
+  end
+end
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 4464c51744aa77199b331d8effc9293ffbc8027a..1787caa243d4a1226a13b59271122b8449020bcf 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,9 +1,12 @@
+= content_for :meta_tags do
+  - if current_user
+    = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity")
+
 - if current_user && can?(current_user, :download_code, @project)
   = render 'shared/no_ssh'
   = render 'shared/no_password'
 
 = render "home_panel"
-
 %ul.nav.nav-tabs
   %li.active
     = link_to '#tab-activity', 'data-toggle' => 'tab' do
@@ -13,11 +16,11 @@
       = link_to '#tab-readme', 'data-toggle' => 'tab' do
         Readme
   - if @repository.changelog
-    %li
+    %li.hidden-xs
       = link_to changelog_url(@project) do
         Changelog
   - if @repository.contribution_guide
-    %li
+    %li.hidden-xs
       = link_to contribution_guide_url(@project) do
         Contribution guide
   - if @repository.license
@@ -38,8 +41,18 @@
       = link_to '#aside', class: 'show-aside' do
         %i.fa.fa-angle-left
       %section.col-md-9
-        = render "events/event_last_push", event: @last_push
-        = render 'shared/event_filter'
+        .hidden-xs
+          = render "events/event_last_push", event: @last_push
+
+          - if current_user
+            %ul.nav.nav-pills.event_filter.pull-right
+              %li
+                = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'rss-btn' do
+                  %i.fa.fa-rss
+                  Activity Feed
+
+          = render 'shared/event_filter'
+          %hr
         .content_list
         = spinner
       %aside.col-md-3.project-side
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
index 2d4d5d030ab3ec37c7601b4dc9d114bfec25621f..7baddebde455944aebc557848ab5f25409b2ef87 100644
--- a/app/views/projects/snippets/edit.html.haml
+++ b/app/views/projects/snippets/edit.html.haml
@@ -1,4 +1,4 @@
 %h3.page-title
   Edit snippet
 %hr
-= render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet)
+= render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet), visibility_level: @snippet.visibility_level
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
index bb659dba0cf1cb21504f6796f835193055565305..5efe662665ee72824a0ef2e9d8904161491cfe51 100644
--- a/app/views/projects/snippets/new.html.haml
+++ b/app/views/projects/snippets/new.html.haml
@@ -1,4 +1,4 @@
 %h3.page-title
   New snippet
 %hr
-= render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet)
+= render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet), visibility_level: default_snippet_visibility
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index feca145369752b8ad9e3758288dd7a33986d1e83..a8a580944e1d7c0fea60a60f3413141463c107e6 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -1,3 +1,7 @@
+= content_for :meta_tags do
+  - if current_user
+    = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
+  
 .tree-ref-holder
   = render 'shared/ref_switcher', destination: 'tree', path: @path
 
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index d07a9e2b92468860f9b77d3345b27dc6fff5e97c..334db60690d7fd4f1c6d2d5cace134a9be789093 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -3,17 +3,3 @@
   = event_filter_link EventFilter.merged, 'Merge events'
   = event_filter_link EventFilter.comments, 'Comments'
   = event_filter_link EventFilter.team, 'Team'
-
-  - if current_user
-    - if current_controller?(:dashboard)
-      %li.pull-right
-        = link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
-          %i.fa.fa-rss
-          News Feed
-
-    - if current_controller?(:groups)
-      %li.pull-right
-        = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
-          %i.fa.fa-rss
-          News Feed
-%hr
diff --git a/app/views/shared/_issuable_filter.html.haml b/app/views/shared/_issuable_filter.html.haml
index 83f5a3a8015ef276467f55072924b803f2b888e6..f9eb2dcfa282a18eec598a2b7186a435102623b5 100644
--- a/app/views/shared/_issuable_filter.html.haml
+++ b/app/views/shared/_issuable_filter.html.haml
@@ -4,15 +4,15 @@
       %li{class: ("active" if params[:state] == 'opened')}
         = link_to page_filter_path(state: 'opened') do
           %i.fa.fa-exclamation-circle
-          Open
+          #{state_filters_text_for(:opened, @project)}
       %li{class: ("active" if params[:state] == 'closed')}
         = link_to page_filter_path(state: 'closed') do
           %i.fa.fa-check-circle
-          Closed
+          #{state_filters_text_for(:closed, @project)}
       %li{class: ("active" if params[:state] == 'all')}
         = link_to page_filter_path(state: 'all') do
           %i.fa.fa-compass
-          All
+          #{state_filters_text_for(:all, @project)}
 
   .issues-details-filters
     = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_id, :label_name]), method: :get, class: 'filter-form' do
diff --git a/app/views/shared/_visibility_level.html.haml b/app/views/shared/_visibility_level.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..1c6ec198d3d460425f73702aa7cbb92eeb303623
--- /dev/null
+++ b/app/views/shared/_visibility_level.html.haml
@@ -0,0 +1,14 @@
+.form-group.project-visibility-level-holder
+  = f.label :visibility_level, class: 'control-label' do
+    Visibility Level
+    = link_to "(?)", help_page_path("public_access", "public_access")
+  .col-sm-10
+    - if can_change_visibility_level
+      = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: visibility_level, form_model: form_model)
+    - else
+      .col-sm-10
+        %span.info
+          = visibility_level_icon(visibility_level)
+          %strong
+            = visibility_level_label(visibility_level)
+          .light= visibility_level_description(visibility_level, form_model)
diff --git a/app/views/shared/_visibility_radios.html.haml b/app/views/shared/_visibility_radios.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..b07c4d20f12cade024feb47c21e063a6e54dfec1
--- /dev/null
+++ b/app/views/shared/_visibility_radios.html.haml
@@ -0,0 +1,14 @@
+- Gitlab::VisibilityLevel.values.each do |level|
+  .radio
+    - restricted = restricted_visibility_levels.include?(level)
+    = label model_method, level do
+      = form.radio_button model_method, level, checked: (selected_level == level), disabled: restricted
+      = visibility_level_icon(level)
+      .option-title
+        = visibility_level_label(level)
+      .option-descr
+        = visibility_level_description(level, form_model)
+- unless restricted_visibility_levels.empty?
+  .col-sm-10
+    %span.info
+      Some visibility level settings have been restricted by the administrator.
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 4e0663ea2080b087a3226124394d2fddf324925a..6783587bda948e82f70a0adb00b3795529498f83 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -10,7 +10,7 @@
       = f.label :title, class: 'control-label'
       .col-sm-10= f.text_field :title, placeholder: "Example Snippet", class: 'form-control', required: true
 
-    = render "shared/snippets/visibility_level", f: f, visibility_level: gitlab_config.default_projects_features.visibility_level, can_change_visibility_level: true
+    = render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: true, form_model: @snippet
     
     .form-group
       .file-editor
diff --git a/app/views/shared/snippets/_visibility_level.html.haml b/app/views/shared/snippets/_visibility_level.html.haml
deleted file mode 100644
index 9acff18e450f2302e69440df80273a5b73c18ad6..0000000000000000000000000000000000000000
--- a/app/views/shared/snippets/_visibility_level.html.haml
+++ /dev/null
@@ -1,27 +0,0 @@
-.form-group.project-visibility-level-holder
-  = f.label :visibility_level, class: 'control-label' do
-    Visibility Level
-    = link_to "(?)", help_page_path("public_access", "public_access")
-  .col-sm-10
-    - if can_change_visibility_level
-      - Gitlab::VisibilityLevel.values.each do |level|
-        .radio
-          - restricted = restricted_visibility_levels.include?(level)
-          = f.radio_button :visibility_level, level, disabled: restricted
-          = label "#{dom_class(@snippet)}_visibility_level", level do
-            = visibility_level_icon(level)
-            .option-title
-              = visibility_level_label(level)
-            .option-descr
-              = snippet_visibility_level_description(level)
-      - unless restricted_visibility_levels.empty?
-        .col-sm-10
-          %span.info
-            Some visibility level settings have been restricted by the administrator.
-    - else
-      .col-sm-10
-        %span.info
-          = visibility_level_icon(visibility_level)
-          %strong
-            = visibility_level_label(visibility_level)
-          .light= visibility_level_description(visibility_level)
diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml
index 7042d07d5e8bbacf6ba445639e92ecf83a722480..30aa174edfb3329b039c4da52dabf4982ba9e4c9 100644
--- a/app/views/snippets/edit.html.haml
+++ b/app/views/snippets/edit.html.haml
@@ -1,4 +1,4 @@
 %h3.page-title
   Edit snippet
 %hr
-= render "shared/snippets/form", url: snippet_path(@snippet)
+= render 'shared/snippets/form', url: snippet_path(@snippet), visibility_level: @snippet.visibility_level
diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml
index 694d70583173a69cc826c54d853bf2b51f1a9d8a..77cfd9af335ab8afde5915e4ecc64a130d547e35 100644
--- a/app/views/snippets/new.html.haml
+++ b/app/views/snippets/new.html.haml
@@ -1,4 +1,4 @@
 %h3.page-title
   New snippet
 %hr
-= render "shared/snippets/form", url: snippets_path(@snippet)
+= render "shared/snippets/form", url: snippets_path(@snippet), visibility_level: default_snippet_visibility
diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder
index 8fe30b23635fd58e414485a3a3510c36b135effe..50232dc7186054179c5a5477948e86507079819c 100644
--- a/app/views/users/show.atom.builder
+++ b/app/views/users/show.atom.builder
@@ -1,9 +1,9 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
-  xml.title   "Activity feed for #{@user.name}"
+  xml.title   "#{@user.name} activity"
   xml.link    href: user_url(@user, :atom), rel: "self", type: "application/atom+xml"
   xml.link    href: user_url(@user), rel: "alternate", type: "text/html"
-  xml.id      projects_url
+  xml.id      user_url(@user)
   xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
 
   @events.each do |event|
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 9dd8cb0738c55bb681aca50603a100e6369d82e2..15a3f741e6cd990c575a60b6c91535ea4df28274 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,3 +1,6 @@
+= content_for :meta_tags do
+  = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
+
 .row
   = link_to '#aside', class: 'show-aside' do
     %i.fa.fa-angle-left
diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb
index 8b50f423984f8f452b0c8ada50d6250143a6d7fb..84a54656df25d7c5982c7361138e889c5cbfff5f 100644
--- a/app/workers/irker_worker.rb
+++ b/app/workers/irker_worker.rb
@@ -137,8 +137,7 @@ def send_one_commit(project, hook_attrs, repo_name, branch)
   end
 
   def commit_from_id(project, id)
-    commit = Gitlab::Git::Commit.find(project.repository, id)
-    Commit.new(commit)
+    project.commit(id)
   end
 
   def files_count(commit)
diff --git a/app/workers/project_web_hook_worker.rb b/app/workers/project_web_hook_worker.rb
index 73085c046bd6ab50ab3183a7178ab196939f3ad9..fb87896528889c245185fb95adb1761612bea26a 100644
--- a/app/workers/project_web_hook_worker.rb
+++ b/app/workers/project_web_hook_worker.rb
@@ -3,8 +3,8 @@ class ProjectWebHookWorker
 
   sidekiq_options queue: :project_web_hook
 
-  def perform(hook_id, data)
+  def perform(hook_id, data, hook_name)
     data = data.with_indifferent_access
-    WebHook.find(hook_id).execute(data)
+    WebHook.find(hook_id).execute(data, hook_name)
   end
 end
diff --git a/app/workers/system_hook_worker.rb b/app/workers/system_hook_worker.rb
index 3ebc62b7e7a5a890d01f65b492b14ba6b76d60aa..a122c274763ad0ce41118de0499d11595379802f 100644
--- a/app/workers/system_hook_worker.rb
+++ b/app/workers/system_hook_worker.rb
@@ -3,7 +3,7 @@ class SystemHookWorker
 
   sidekiq_options queue: :system_hook
 
-  def perform(hook_id, data)
-    SystemHook.find(hook_id).execute data
+  def perform(hook_id, data, hook_name)
+    SystemHook.find(hook_id).execute(data, hook_name)
   end
 end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 66f13a329c518c348015a9b743a4f6a6e1d4885d..84271f2c30a0a2d13d39e57d2896bce84eb0bb68 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -76,7 +76,6 @@ production: &base
       merge_requests: true
       wiki: true
       snippets: false
-      visibility_level: "private"  # can be "private" | "internal" | "public"
 
     ## Webhook settings
     # Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10)
diff --git a/config/routes.rb b/config/routes.rb
index 843dfe2b29e3cdcd5a2603951d25484203125360..81d8cac1022bb58b212961bccde2f1608241c20a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -252,7 +252,7 @@
       constraints: { username: /.*/ }
 
   get '/u/:username' => 'users#show', as: :user,
-      constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
+      constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }
 
   #
   # Dashboard Area
@@ -279,7 +279,7 @@
   #
   # Groups Area
   #
-  resources :groups, constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }  do
+  resources :groups, constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }  do
     member do
       get :issues
       get :merge_requests
@@ -328,7 +328,7 @@
   # Project Area
   #
   resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do
-    resources(:projects, constraints: { id: /[a-zA-Z.0-9_\-]+/ }, except:
+    resources(:projects, constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }, except:
               [:new, :create, :index], path: "/") do
       member do
         put :transfer
diff --git a/db/migrate/20150421120000_remove_periods_at_ends_of_usernames.rb b/db/migrate/20150421120000_remove_periods_at_ends_of_usernames.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3057ea3c68c22f89141948d475662b2df4a90ca8
--- /dev/null
+++ b/db/migrate/20150421120000_remove_periods_at_ends_of_usernames.rb
@@ -0,0 +1,88 @@
+class RemovePeriodsAtEndsOfUsernames < ActiveRecord::Migration
+  include Gitlab::ShellAdapter
+
+  class Namespace < ActiveRecord::Base
+    class << self
+      def find_by_path_or_name(path)
+        find_by("lower(path) = :path OR lower(name) = :path", path: path.downcase)
+      end
+
+      def clean_path(path)
+        path = path.dup
+        # Get the email username by removing everything after an `@` sign.
+        path.gsub!(/@.*\z/,             "")
+        # Usernames can't end in .git, so remove it.
+        path.gsub!(/\.git\z/,           "")
+        # Remove dashes at the start of the username.
+        path.gsub!(/\A-+/,              "")
+        # Remove periods at the end of the username.
+        path.gsub!(/\.+\z/,             "")
+        # Remove everything that's not in the list of allowed characters.
+        path.gsub!(/[^a-zA-Z0-9_\-\.]/, "")
+
+        # Users with the great usernames of "." or ".." would end up with a blank username.
+        # Work around that by setting their username to "blank", followed by a counter.
+        path = "blank" if path.blank?
+
+        counter = 0
+        base = path
+        while Namespace.find_by_path_or_name(path)
+          counter += 1
+          path = "#{base}#{counter}"
+        end
+
+        path
+      end
+    end
+  end
+
+  def up
+    changed_paths = {}
+
+    select_all("SELECT id, username FROM users WHERE username LIKE '%.'").each do |user|
+      username_was = user["username"]
+      username = Namespace.clean_path(username_was)
+      changed_paths[username_was] = username
+
+      username = quote_string(username)
+      execute "UPDATE users SET username = '#{username}' WHERE id = #{user["id"]}"
+      execute "UPDATE namespaces SET path = '#{username}', name = '#{username}' WHERE type IS NULL AND owner_id = #{user["id"]}"
+    end
+
+    select_all("SELECT id, path FROM namespaces WHERE type = 'Group' AND path LIKE '%.'").each do |group|
+      path_was = group["path"]
+      path = Namespace.clean_path(path_was)
+      changed_paths[path_was] = path
+
+      path = quote_string(path)
+      execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{group["id"]}"
+    end
+
+    changed_paths.each do |path_was, path|
+      # Don't attempt to move if original path only contains periods.
+      next if path_was =~ /\A\.+\z/
+
+      if gitlab_shell.mv_namespace(path_was, path)
+        # If repositories moved successfully we need to remove old satellites
+        # and send update instructions to users.
+        # However we cannot allow rollback since we moved namespace dir
+        # So we basically we mute exceptions in next actions
+        begin
+          gitlab_shell.rm_satellites(path_was)
+          # We cannot send update instructions since models and mailers
+          # can't safely be used from migrations as they may be written for 
+          # later versions of the database.
+          # send_update_instructions
+        rescue
+          # Returning false does not rollback after_* transaction but gives
+          # us information about failing some of tasks
+          false
+        end
+      else
+        # if we cannot move namespace directory we should rollback
+        # db changes in order to prevent out of sync between db and fs
+        raise Exception.new('namespace directory cannot be moved')
+      end
+    end
+  end
+end
diff --git a/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9b0f13f3fa7e62929b4d2f57b1148647a96b3a4a
--- /dev/null
+++ b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb
@@ -0,0 +1,7 @@
+class AddDefaultProjectVisibililtyToApplicationSettings < ActiveRecord::Migration
+  def change
+    add_column :application_settings, :default_project_visibility, :integer
+    visibility = Settings.gitlab.default_projects_features['visibility_level']
+    execute("update application_settings set default_project_visibility = #{visibility}")
+  end
+end
diff --git a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..51237354d9f7fc39490f2a47f4c73582e40f6b32
--- /dev/null
+++ b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb
@@ -0,0 +1,7 @@
+class AddDefaultSnippetVisibilityToAppSettings < ActiveRecord::Migration
+  def change
+    add_column :application_settings, :default_snippet_visibility, :integer
+    visibility = Settings.gitlab.default_projects_features['visibility_level']
+    execute("update application_settings set default_snippet_visibility = #{visibility}")
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 7e101ca142291a0a48301c4d992abb41aa4d2174..6728c211abc7da5db60b7b4542c7404bcfda32a4 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: 20150417122318) do
+ActiveRecord::Schema.define(version: 20150425173433) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -41,6 +41,8 @@
     t.text     "help_text"
     t.text     "restricted_visibility_levels"
     t.integer  "max_attachment_size",          default: 10,   null: false
+    t.integer  "default_project_visibility"
+    t.integer  "default_snippet_visibility"
   end
 
   create_table "audit_events", force: true do |t|
diff --git a/doc/integration/gitlab_buttons_in_gmail.md b/doc/integration/gitlab_buttons_in_gmail.md
index a9885cef1092ce710ffb7981dd7c810b11329903..e35bb8ba6936da6a25087b8da671d5742218b5f7 100644
--- a/doc/integration/gitlab_buttons_in_gmail.md
+++ b/doc/integration/gitlab_buttons_in_gmail.md
@@ -7,11 +7,11 @@ If correctly setup, emails that require an action will be marked in Gmail.
 ![gitlab_actions](gitlab_actions.png)
 
 To get this functioning, you need to be registered with Google.
-[See how to register with google in this document.](https://developers.google.com/gmail/markup/registering-with-google)
+[See how to register with Google in this document.](https://developers.google.com/gmail/markup/registering-with-google)
 
-To aid the registering with google, GitLab offers a rake task that will send an email to google whitelisting email address from your GitLab server.
+To aid the registering with Google, GitLab offers a rake task that will send an email to Google whitelisting email address from your GitLab server.
 
-To check what would be sent to the google email address, run the rake task:
+To check what would be sent to the Google email address, run the rake task:
 
 ```bash
 bundle exec rake gitlab:mail_google_schema_whitelisting RAILS_ENV=production
@@ -19,7 +19,7 @@ bundle exec rake gitlab:mail_google_schema_whitelisting RAILS_ENV=production
 
 **This will not send the email but give you the output of how the mail will look.**
 
-Copy the output of the rake task to [google email markup tester](https://www.google.com/webmasters/markup-tester/u/0/) and press "Validate".
+Copy the output of the rake task to [Google email markup tester](https://www.google.com/webmasters/markup-tester/u/0/) and press "Validate".
 
 If you receive "No errors detected" message from the tester you can send the email using:
 
diff --git a/features/admin/settings.feature b/features/admin/settings.feature
index 89e85125daf256df3ad8cfaeac736a1d5c550ef8..5a5d2afeeb0b6bf13eb325fb8daff2687e20386e 100644
--- a/features/admin/settings.feature
+++ b/features/admin/settings.feature
@@ -19,6 +19,9 @@ Feature: Admin Settings
   Scenario: Change Slack Service Template settings
     When I click on "Service Templates"
     And I click on "Slack" service
+    And I fill out Slack settings
     Then I check all events and submit form
     And I should see service template settings saved
+    Then I click on "Slack" service
     And I should see all checkboxes checked
+    And I should see Slack settings saved
diff --git a/features/project/team_management.feature b/features/project/team_management.feature
index 2ecaa5d694f7542a5280253be9a587f6479f25b2..e5de4d819f0df8ab73526a64ea75183ed10f623a 100644
--- a/features/project/team_management.feature
+++ b/features/project/team_management.feature
@@ -3,13 +3,13 @@ Feature: Project Team Management
     Given I sign in as a user
     And I own project "Shop"
     And gitlab user "Mike"
-    And gitlab user "Sam"
-    And "Sam" is "Shop" developer
+    And gitlab user "Dmitriy"
+    And "Dmitriy" is "Shop" developer
     And I visit project "Shop" team page
 
   Scenario: See all team members
     Then I should be able to see myself in team
-    And I should see "Sam" in team list
+    And I should see "Dmitriy" in team list
 
   @javascript
   Scenario: Add user to project
@@ -25,14 +25,14 @@ Feature: Project Team Management
 
   @javascript
   Scenario: Update user access
-    Given I should see "Sam" in team list as "Developer"
-    And I change "Sam" role to "Reporter"
-    And I should see "Sam" in team list as "Reporter"
+    Given I should see "Dmitriy" in team list as "Developer"
+    And I change "Dmitriy" role to "Reporter"
+    And I should see "Dmitriy" in team list as "Reporter"
 
   Scenario: Cancel team member
-    Given I click cancel link for "Sam"
+    Given I click cancel link for "Dmitriy"
     Then I visit project "Shop" team page
-    And I should not see "Sam" in team list
+    And I should not see "Dmitriy" in team list
 
   Scenario: Import team from another project
     Given I own project "Website"
diff --git a/features/steps/admin/settings.rb b/features/steps/admin/settings.rb
index 4f6604c2995e6b38a268b30341095588cf6c1deb..39a4849836505ae31861f292381a97cd1d73f0e3 100644
--- a/features/steps/admin/settings.rb
+++ b/features/steps/admin/settings.rb
@@ -44,10 +44,15 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
     page.check('Comments')
     page.check('Issues events')
     page.check('Merge Request events')
-    fill_in 'Webhook', with: "http://localhost"
     click_on 'Save'
   end
 
+  step 'I fill out Slack settings' do
+    fill_in 'Webhook', with: 'http://localhost'
+    fill_in 'Username', with: 'test_user'
+    fill_in 'Channel', with: '#test_channel'
+  end
+
   step 'I should see service template settings saved' do
     page.should have_content 'Application settings saved successfully'
   end
@@ -58,6 +63,12 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
     end
   end
 
+  step 'I should see Slack settings saved' do
+    find_field('Webhook').value.should eq 'http://localhost'
+    find_field('Username').value.should eq 'test_user'
+    find_field('Channel').value.should eq '#test_channel'
+  end
+
   def help_text
     'For help related to GitLab contact Marc Smith at marc@smith.example or find him in office 42.'
   end
diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb
index 5e588ceb780c7f4196ad2702a0adbf6b81370662..93456a81ecf7838006a3db256463713555c3fa6d 100644
--- a/features/steps/dashboard/new_project.rb
+++ b/features/steps/dashboard/new_project.rb
@@ -4,7 +4,9 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
   include SharedProject
 
   step 'I click "New project" link' do
-    click_link "New project"
+    within('.content') do
+      click_link "New project"
+    end
   end
 
   step 'I see "New project" page' do
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index 57b727f837ed4d007f881f9c7dc551a16d3c7077..30b1934b3630df173da4e4d74279f84e4e39e968 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -18,7 +18,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
   step 'I see commits atom feed' do
     commit = @project.repository.commit
     response_headers['Content-Type'].should have_content("application/atom+xml")
-    body.should have_selector("title", text: "Recent commits to #{@project.name}")
+    body.should have_selector("title", text: "#{@project.name}:master commits")
     body.should have_selector("author email", text: commit.author_email)
     body.should have_selector("entry summary", text: commit.description[0..10])
   end
diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb
index 1f2e863f8ca86dbb73e29796faa9033e4b5c2856..e01f712c18c3a4291745e02591a768448c273a0a 100644
--- a/features/steps/project/team_management.rb
+++ b/features/steps/project/team_management.rb
@@ -9,8 +9,8 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
     page.should have_content(@user.username)
   end
 
-  step 'I should see "Sam" in team list' do
-    user = User.find_by(name: "Sam")
+  step 'I should see "Dmitriy" in team list' do
+    user = User.find_by(name: "Dmitriy")
     page.should have_content(user.name)
     page.should have_content(user.username)
   end
@@ -51,15 +51,15 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
     end
   end
 
-  step 'I should see "Sam" in team list as "Developer"' do
+  step 'I should see "Dmitriy" in team list as "Developer"' do
     within ".access-developer" do
-      page.should have_content('Sam')
+      page.should have_content('Dmitriy')
     end
   end
 
-  step 'I change "Sam" role to "Reporter"' do
+  step 'I change "Dmitriy" role to "Reporter"' do
     project = Project.find_by(name: "Shop")
-    user = User.find_by(name: 'Sam')
+    user = User.find_by(name: 'Dmitriy')
     project_member = project.project_members.find_by(user_id: user.id)
     within "#project_member_#{project_member.id}" do
       click_button "Edit access level"
@@ -68,9 +68,9 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
     end
   end
 
-  step 'I should see "Sam" in team list as "Reporter"' do
+  step 'I should see "Dmitriy" in team list as "Reporter"' do
     within ".access-reporter" do
-      page.should have_content('Sam')
+      page.should have_content('Dmitriy')
     end
   end
 
@@ -78,8 +78,8 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
     click_link "Remove from team"
   end
 
-  step 'I should not see "Sam" in team list' do
-    user = User.find_by(name: "Sam")
+  step 'I should not see "Dmitriy" in team list' do
+    user = User.find_by(name: "Dmitriy")
     page.should_not have_content(user.name)
     page.should_not have_content(user.username)
   end
@@ -88,12 +88,12 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
     create(:user, name: "Mike")
   end
 
-  step 'gitlab user "Sam"' do
-    create(:user, name: "Sam")
+  step 'gitlab user "Dmitriy"' do
+    create(:user, name: "Dmitriy")
   end
 
-  step '"Sam" is "Shop" developer' do
-    user = User.find_by(name: "Sam")
+  step '"Dmitriy" is "Shop" developer' do
+    user = User.find_by(name: "Dmitriy")
     project = Project.find_by(name: "Shop")
     project.team << [user, :developer]
   end
@@ -119,9 +119,9 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
     click_button 'Import'
   end
 
-  step 'I click cancel link for "Sam"' do
+  step 'I click cancel link for "Dmitriy"' do
     project = Project.find_by(name: "Shop")
-    user = User.find_by(name: 'Sam')
+    user = User.find_by(name: 'Dmitriy')
     project_member = project.project_members.find_by(user_id: user.id)
     within "#project_member_#{project_member.id}" do
       click_link('Remove user from team')
diff --git a/features/support/capybara.rb b/features/support/capybara.rb
new file mode 100644
index 0000000000000000000000000000000000000000..31dbf0feb2fc57897ad34de32426bc7e8ae65e31
--- /dev/null
+++ b/features/support/capybara.rb
@@ -0,0 +1,24 @@
+require 'spinach/capybara'
+require 'capybara/poltergeist'
+
+# Give CI some extra time
+timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 90 : 10
+
+Capybara.javascript_driver = :poltergeist
+Capybara.register_driver :poltergeist do |app|
+  Capybara::Poltergeist::Driver.new(app, js_errors: true, timeout: timeout)
+end
+
+Spinach.hooks.on_tag("javascript") do
+  Capybara.current_driver = Capybara.javascript_driver
+end
+
+Capybara.default_wait_time = timeout
+Capybara.ignore_hidden_elements = false
+
+unless ENV['CI'] || ENV['CI_SERVER']
+  require 'capybara-screenshot/spinach'
+
+  # Keep only the screenshots generated from the last failing test suite
+  Capybara::Screenshot.prune_strategy = :keep_last_run
+end
diff --git a/features/support/db_cleaner.rb b/features/support/db_cleaner.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1ab308cfa556f4c640c5b4a51b00ae6d5a571aed
--- /dev/null
+++ b/features/support/db_cleaner.rb
@@ -0,0 +1,11 @@
+require 'database_cleaner'
+
+DatabaseCleaner.strategy = :truncation
+
+Spinach.hooks.before_scenario do
+  DatabaseCleaner.start
+end
+
+Spinach.hooks.after_scenario do
+  DatabaseCleaner.clean
+end
diff --git a/features/support/env.rb b/features/support/env.rb
index be17065ccfd728ddb1c1b171dd75fa901e003e0d..f34302721ed0cc3d13ed373203d6196817f18c51 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -11,40 +11,18 @@
 require './config/environment'
 require 'rspec'
 require 'rspec/expectations'
-require 'database_cleaner'
-require 'spinach/capybara'
 require 'sidekiq/testing/inline'
 
+require_relative 'capybara'
+require_relative 'db_cleaner'
+
 %w(select2_helper test_env repo_helpers).each do |f|
   require Rails.root.join('spec', 'support', f)
 end
 
-Dir["#{Rails.root}/features/steps/shared/*.rb"].each {|file| require file}
+Dir["#{Rails.root}/features/steps/shared/*.rb"].each { |file| require file }
 
 WebMock.allow_net_connect!
-#
-# JS driver
-#
-require 'capybara/poltergeist'
-Capybara.javascript_driver = :poltergeist
-Capybara.register_driver :poltergeist do |app|
-  Capybara::Poltergeist::Driver.new(app, js_errors: false, timeout: 90)
-end
-Spinach.hooks.on_tag("javascript") do
-  ::Capybara.current_driver = ::Capybara.javascript_driver
-end
-Capybara.default_wait_time = 60
-Capybara.ignore_hidden_elements = false
-
-DatabaseCleaner.strategy = :truncation
-
-Spinach.hooks.before_scenario do
-  DatabaseCleaner.start
-end
-
-Spinach.hooks.after_scenario do
-  DatabaseCleaner.clean
-end
 
 Spinach.hooks.before_run do
   include RSpec::Mocks::ExampleMethods
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 0de4e720ffe00ca8c8af9b0746e234b7d0ac3902..23270b1c0f46529a1482eea5507009a5c9757870 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -32,7 +32,7 @@ class Commits < Grape::API
       #   GET /projects/:id/repository/commits/:sha
       get ":id/repository/commits/:sha" do
         sha = params[:sha]
-        commit = user_project.repository.commit(sha)
+        commit = user_project.commit(sha)
         not_found! "Commit" unless commit
         present commit, with: Entities::RepoCommitDetail
       end
@@ -46,7 +46,7 @@ class Commits < Grape::API
       #   GET /projects/:id/repository/commits/:sha/diff
       get ":id/repository/commits/:sha/diff" do
         sha = params[:sha]
-        commit = user_project.repository.commit(sha)
+        commit = user_project.commit(sha)
         not_found! "Commit" unless commit
         commit.diffs
       end
@@ -60,7 +60,7 @@ class Commits < Grape::API
       #   GET /projects/:id/repository/commits/:sha/comments
       get ':id/repository/commits/:sha/comments' do
         sha = params[:sha]
-        commit = user_project.repository.commit(sha)
+        commit = user_project.commit(sha)
         not_found! 'Commit' unless commit
         notes = Note.where(commit_id: commit.id)
         present paginate(notes), with: Entities::CommitNote
@@ -81,7 +81,7 @@ class Commits < Grape::API
         required_attributes! [:note]
 
         sha = params[:sha]
-        commit = user_project.repository.commit(sha)
+        commit = user_project.commit(sha)
         not_found! 'Commit' unless commit
         opts = {
           note: params[:note],
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 75f2f5fa2d0f76cc19d3cd7cd5f0d50733348596..fa9aab290011d6cbc1341bc0e6a9fcea157f7310 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -269,11 +269,11 @@ class Label < Grape::Entity
 
     class Compare < Grape::Entity
       expose :commit, using: Entities::RepoCommit do |compare, options|
-        Commit.decorate(compare.commits).last
+        Commit.decorate(compare.commits, nil).last
       end
 
       expose :commits, using: Entities::RepoCommit do |compare, options|
-        Commit.decorate(compare.commits)
+        Commit.decorate(compare.commits, nil)
       end
 
       expose :diffs, using: Entities::RepoDiff do |compare, options|
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 3176ef0e256297e489176aea1a6d92123cbcb60a..e0ea6d7dd1dcedd191c63cdca3582f532973513b 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -34,7 +34,7 @@ class Files < Grape::API
         ref = attrs.delete(:ref)
         file_path = attrs.delete(:file_path)
 
-        commit = user_project.repository.commit(ref)
+        commit = user_project.commit(ref)
         not_found! 'Commit' unless commit
 
         blob = user_project.repository.blob_at(commit.sha, file_path)
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 1fbf3dca3c6faff82f943b72b7b2c1aada99330f..2d96c9666d2a907119fecdfcf8948a19e16a0478 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -62,7 +62,7 @@ def handle_project_member_errors(errors)
         ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
         path = params[:path] || nil
 
-        commit = user_project.repository.commit(ref)
+        commit = user_project.commit(ref)
         not_found!('Tree') unless commit
 
         tree = user_project.repository.tree(commit.id, path)
diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb
index 518964db50d3739a5472771b9f1f3a8e0ab9b1e4..22b8f90dc5c6b95fc5fc0bdf05c2f744df19f7d9 100644
--- a/lib/api/system_hooks.rb
+++ b/lib/api/system_hooks.rb
@@ -47,7 +47,7 @@ class SystemHooks < Grape::API
           owner_name: "Someone",
           owner_email: "example@gitlabhq.com"
         }
-        @hook.execute(data)
+        @hook.execute(data, 'system_hooks')
         data
       end
 
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index 3fd0823df063662b5f05ead5446e6c4897200ba5..45bb904ed7afacefd386483c0a37c74887e458d6 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -17,7 +17,7 @@ def timestamps
       events = Event.reorder(nil).contributions.where(author_id: user.id).
         where("created_at > ?", date_from).where(project_id: projects).
         group('date(created_at)').
-        select('date(created_at), count(id) as total_amount').
+        select('date(created_at) as date, count(id) as total_amount').
         map(&:attributes)
 
       dates = (1.year.ago.to_date..(Date.today + 1.day)).to_a
diff --git a/lib/gitlab/gitorious_import/client.rb b/lib/gitlab/gitorious_import/client.rb
index 8cdc3d4afae73a9434b72b3547393eb35de255a0..1fa89dba448727912843b0adeb3badcdb3325f06 100644
--- a/lib/gitlab/gitorious_import/client.rb
+++ b/lib/gitlab/gitorious_import/client.rb
@@ -14,7 +14,7 @@ def authorize_url(redirect_uri)
       end
 
       def repos
-        @repos ||= repo_names.map { |full_name| Repository.new(full_name) }
+        @repos ||= repo_names.map { |full_name| GitoriousImport::Repository.new(full_name) }
       end
 
       def repo(id)
diff --git a/lib/gitlab/identifier.rb b/lib/gitlab/identifier.rb
index 6e4de197eeb3b31bbe6ba44c143decea3b811eaf..3e5d728f3bce43ecc99e37db99895f17ee7c0f74 100644
--- a/lib/gitlab/identifier.rb
+++ b/lib/gitlab/identifier.rb
@@ -5,7 +5,7 @@ module Identifier
     def identify(identifier, project, newrev)
       if identifier.blank?
         # Local push from gitlab
-        email = project.repository.commit(newrev).author_email rescue nil
+        email = project.commit(newrev).author_email rescue nil
         User.find_by(email: email) if email
 
       elsif identifier =~ /\Auser-\d+\Z/
diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb
index 1128c1bed7a3eecf47590966ee7e0156f04218a3..8764f7e474f4111730b227172c6b06f69c0aea29 100644
--- a/lib/gitlab/markdown/commit_range_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_range_reference_filter.rb
@@ -32,11 +32,8 @@ def initialize(*args)
 
       # Pattern used to extract commit range references from text
       #
-      # The beginning and ending SHA1 sums can be between 6 and 40 hex
-      # characters, and the range selection can be double- or triple-dot.
-      #
       # This pattern supports cross-project references.
-      COMMIT_RANGE_PATTERN = /(#{PROJECT_PATTERN}@)?(?<commit_range>\h{6,40}\.{2,3}\h{6,40})/
+      COMMIT_RANGE_PATTERN = /(#{PROJECT_PATTERN}@)?(?<commit_range>#{CommitRange::PATTERN})/
 
       def call
         replace_text_nodes_matching(COMMIT_RANGE_PATTERN) do |content|
@@ -53,52 +50,34 @@ def call
       # links have `gfm` and `gfm-commit_range` class names attached for
       # styling.
       def commit_range_link_filter(text)
-        self.class.references_in(text) do |match, commit_range, project_ref|
+        self.class.references_in(text) do |match, id, project_ref|
           project = self.project_from_ref(project_ref)
 
-          from_id, to_id = split_commit_range(commit_range)
+          range = CommitRange.new(id, project)
+
+          if range.valid_commits?
+            push_result(:commit_range, range)
 
-          if valid_range?(project, from_id, to_id)
-            url = url_for_commit_range(project, from_id, to_id)
+            url = url_for_commit_range(project, range)
 
-            title = "Commits #{from_id} through #{to_id}"
+            title = range.reference_title
             klass = reference_class(:commit_range)
 
             project_ref += '@' if project_ref
 
             %(<a href="#{url}"
                  title="#{title}"
-                 class="#{klass}">#{project_ref}#{commit_range}</a>)
+                 class="#{klass}">#{project_ref}#{range}</a>)
           else
             match
           end
         end
       end
 
-      def split_commit_range(range)
-        from_id, to_id = range.split(/\.{2,3}/, 2)
-        from_id << "^" if range !~ /\.{3}/
-
-        [from_id, to_id]
-      end
-
-      def commit(id)
-        unless @commit_map[id]
-          @commit_map[id] = project.repository.commit(id)
-        end
-
-        @commit_map[id]
-      end
-
-      def valid_range?(project, from_id, to_id)
-        project && project.valid_repo? && commit(from_id) && commit(to_id)
-      end
-
-      def url_for_commit_range(project, from_id, to_id)
+      def url_for_commit_range(project, range)
         h = Rails.application.routes.url_helpers
         h.namespace_project_compare_url(project.namespace, project,
-                                        from: from_id, to: to_id,
-                                        only_path: context[:only_path])
+                                        range.to_param.merge(only_path: context[:only_path]))
       end
     end
   end
diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb
index 745de6402cf1ac77c8f3ee5a5375e7ff01012cab..b20b29f5d0ca07b7736242cf8659d926c9c16b00 100644
--- a/lib/gitlab/markdown/commit_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_reference_filter.rb
@@ -48,6 +48,8 @@ def commit_link_filter(text)
           project = self.project_from_ref(project_ref)
 
           if commit = commit_from_ref(project, commit_ref)
+            push_result(:commit, commit)
+
             url = url_for_commit(project, commit)
 
             title = escape_once(commit.link_title)
@@ -57,7 +59,7 @@ def commit_link_filter(text)
 
             %(<a href="#{url}"
                  title="#{title}"
-                 class="#{klass}">#{project_ref}#{commit_ref}</a>)
+                 class="#{klass}">#{project_ref}#{commit.short_id}</a>)
           else
             match
           end
@@ -66,7 +68,7 @@ def commit_link_filter(text)
 
       def commit_from_ref(project, commit_ref)
         if project && project.valid_repo?
-          project.repository.commit(commit_ref)
+          project.commit(commit_ref)
         end
       end
 
diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb
index c9267cc3e9d90c46bd41df347d5fa3f8967d70f4..4b360369d3768f5905c05fd41aa9b275e9706f6f 100644
--- a/lib/gitlab/markdown/issue_reference_filter.rb
+++ b/lib/gitlab/markdown/issue_reference_filter.rb
@@ -48,6 +48,9 @@ def issue_link_filter(text)
           project = self.project_from_ref(project_ref)
 
           if project && project.issue_exists?(issue)
+            # FIXME (rspeicher): Law of Demeter
+            push_result(:issue, project.issues.where(iid: issue).first)
+
             url = url_for_issue(issue, project, only_path: context[:only_path])
 
             title = escape_once("Issue: #{title_for_issue(issue, project)}")
diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb
index 4c21192c0d3cb4ba2cacd4228fc9084e10f97606..a357f28458d5269a0bf8ef6061f004137a69d798 100644
--- a/lib/gitlab/markdown/label_reference_filter.rb
+++ b/lib/gitlab/markdown/label_reference_filter.rb
@@ -52,11 +52,13 @@ def label_link_filter(text)
           params = label_params(id, name)
 
           if label = project.labels.find_by(params)
-            url = url_for_label(project, label)
+            push_result(:label, label)
 
+            url = url_for_label(project, label)
             klass = reference_class(:label)
 
-            %(<a href="#{url}" class="#{klass}">#{render_colored_label(label)}</a>)
+            %(<a href="#{url}"
+                 class="#{klass}">#{render_colored_label(label)}</a>)
           else
             match
           end
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index 40239523cdaa33188a4c436b36b84a7ab0d0b2f3..7c28fe112efcd0b3a8cb3bf9c811b7bb6e1ec50a 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -48,6 +48,8 @@ def merge_request_link_filter(text)
           project = self.project_from_ref(project_ref)
 
           if project && merge_request = project.merge_requests.find_by(iid: id)
+            push_result(:merge_request, merge_request)
+
             title = escape_once("Merge Request: #{merge_request.title}")
             klass = reference_class(:merge_request)
 
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb
index ef4aa408a7e57b8ddd1c7423ca1718538f148506..a4303d96befe2bbedc85564ac33334b568323e41 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/gitlab/markdown/reference_filter.rb
@@ -12,7 +12,15 @@ module Markdown
     #   :reference_class    - Custom CSS class added to reference links.
     #   :only_path          - Generate path-only links.
     #
+    # Results:
+    #   :references - A Hash of references that were found and replaced.
     class ReferenceFilter < HTML::Pipeline::Filter
+      def initialize(*args)
+        super
+
+        result[:references] = Hash.new { |hash, type| hash[type] = [] }
+      end
+
       def escape_once(html)
         ERB::Util.html_escape_once(html)
       end
@@ -29,6 +37,16 @@ def project
         context[:project]
       end
 
+      # Add a reference to the pipeline's result Hash
+      #
+      # type   - Singular Symbol reference type (e.g., :issue, :user, etc.)
+      # values - One or more Objects to add
+      def push_result(type, *values)
+        return if values.empty?
+
+        result[:references][type].push(*values)
+      end
+
       def reference_class(type)
         "gfm gfm-#{type} #{context[:reference_class]}".strip
       end
diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb
index ada67de992b575a6c543f497ffcf3bd53b7ac56b..64a0a2696f7587867fc4d06024695d64223d252e 100644
--- a/lib/gitlab/markdown/snippet_reference_filter.rb
+++ b/lib/gitlab/markdown/snippet_reference_filter.rb
@@ -48,6 +48,8 @@ def snippet_link_filter(text)
           project = self.project_from_ref(project_ref)
 
           if project && snippet = project.snippets.find_by(id: id)
+            push_result(:snippet, snippet)
+
             title = escape_once("Snippet: #{snippet.title}")
             klass = reference_class(:snippet)
 
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb
index 5fc8ed55fe26f114459f52e1ce95f3746b9a1190..28ec041b1d49735855f5380853474a393e6693af 100644
--- a/lib/gitlab/markdown/user_reference_filter.rb
+++ b/lib/gitlab/markdown/user_reference_filter.rb
@@ -38,27 +38,11 @@ def call
       # Returns a String with `@user` references replaced with links. All links
       # have `gfm` and `gfm-project_member` class names attached for styling.
       def user_link_filter(text)
-        project = context[:project]
-
-        self.class.references_in(text) do |match, user|
-          klass = reference_class(:project_member)
-
-          if user == 'all'
-            url = link_to_all(project)
-
-            %(<a href="#{url}" class="#{klass}">@#{user}</a>)
-          elsif namespace = Namespace.find_by(path: user)
-            if namespace.is_a?(Group)
-              if user_can_reference_group?(namespace)
-                url = group_url(user, only_path: context[:only_path])
-                %(<a href="#{url}" class="#{klass}">@#{user}</a>)
-              else
-                match
-              end
-            else
-              url = user_url(user, only_path: context[:only_path])
-              %(<a href="#{url}" class="#{klass}">@#{user}</a>)
-            end
+        self.class.references_in(text) do |match, username|
+          if username == 'all'
+            link_to_all
+          elsif namespace = Namespace.find_by(path: username)
+            link_to_namespace(namespace) || match
           else
             match
           end
@@ -71,17 +55,46 @@ def urls
         Rails.application.routes.url_helpers
       end
 
-      def group_url(*args)
-        urls.group_url(*args)
+      def link_class
+        reference_class(:project_member)
+      end
+
+      def link_to_all
+        project = context[:project]
+
+        # FIXME (rspeicher): Law of Demeter
+        push_result(:user, *project.team.members.flatten)
+
+        url = urls.namespace_project_url(project.namespace, project,
+                                         only_path: context[:only_path])
+
+        %(<a href="#{url}" class="#{link_class}">@all</a>)
+      end
+
+      def link_to_namespace(namespace)
+        if namespace.is_a?(Group)
+          link_to_group(namespace.path, namespace)
+        else
+          link_to_user(namespace.path, namespace)
+        end
       end
 
-      def user_url(*args)
-        urls.user_url(*args)
+      def link_to_group(group, namespace)
+        return unless user_can_reference_group?(namespace)
+
+        push_result(:user, *namespace.users)
+
+        url = urls.group_url(group, only_path: context[:only_path])
+
+        %(<a href="#{url}" class="#{link_class}">@#{group}</a>)
       end
 
-      def link_to_all(project)
-        urls.namespace_project_url(project.namespace, project,
-                                   only_path: context[:only_path])
+      def link_to_user(user, namespace)
+        push_result(:user, namespace.owner)
+
+        url = urls.user_url(user, only_path: context[:only_path])
+
+        %(<a href="#{url}" class="#{link_class}">@#{user}</a>)
       end
 
       def user_can_reference_group?(group)
diff --git a/lib/gitlab/note_data_builder.rb b/lib/gitlab/note_data_builder.rb
index 644dec45dca6db0eec39da424f2d443c2a2abc27..ea6b0ee796dd86a8a01b2c3df3b24c6d501a268e 100644
--- a/lib/gitlab/note_data_builder.rb
+++ b/lib/gitlab/note_data_builder.rb
@@ -69,8 +69,8 @@ def build_base_data(project, user, note)
 
       def build_data_for_commit(project, user, note)
         # commit_id is the SHA hash
-        commit = project.repository.commit(note.commit_id)
-        commit.hook_attrs(project)
+        commit = project.commit(note.commit_id)
+        commit.hook_attrs
       end
     end
   end
diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb
index f8da452e4c07cf676294419fa044679095020cbe..f97784f5abb7e88495bbe9b9c44f8eeb201e93a6 100644
--- a/lib/gitlab/push_data_builder.rb
+++ b/lib/gitlab/push_data_builder.rb
@@ -30,9 +30,7 @@ def build(project, user, oldrev, newrev, ref, commits = [], message = nil)
         
         # For performance purposes maximum 20 latest commits
         # will be passed as post receive hook data.
-        commit_attrs = commits_limited.map do |commit|
-          commit.hook_attrs(project)
-        end
+        commit_attrs = commits_limited.map(&:hook_attrs)
 
         type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push"
         # Hash to be passed as post_receive_data
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 13b6f508e4f508176c24bcccb1b9154e12503c3a..1856872fb5ff57d67dee2e51b9868dd2943a8424 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -8,153 +8,80 @@ def initialize(project, current_user = nil)
       @current_user = current_user
     end
 
-    def can?(user, action, subject)
-      Ability.abilities.allowed?(user, action, subject)
-    end
-
     def analyze(text)
-      text = text.dup
-
-      # Remove preformatted/code blocks so that references are not included
-      text.gsub!(/^```.*?^```/m, '')
-      text.gsub!(/[^`]`[^`]*?`[^`]/, '')
-
-      @references = Hash.new { |hash, type| hash[type] = [] }
-      parse_references(text)
+      @_text = text.dup
     end
 
-    # Given a valid project, resolve the extracted identifiers of the requested type to
-    # model objects.
-
     def users
-      references[:user].uniq.map do |project, identifier|
-        if identifier == "all"
-          project.team.members.flatten
-        elsif namespace = Namespace.find_by(path: identifier)
-          if namespace.is_a?(Group)
-            namespace.users if can?(current_user, :read_group, namespace)
-          else
-            namespace.owner
-          end
-        end
-      end.flatten.compact.uniq
+      result = pipeline_result(:user)
+      result.uniq
     end
 
     def labels
-      references[:label].uniq.map do |project, identifier|
-        project.labels.where(id: identifier).first
-      end.compact.uniq
+      result = pipeline_result(:label)
+      result.uniq
     end
 
     def issues
-      references[:issue].uniq.map do |project, identifier|
-        if project.default_issues_tracker?
-          project.issues.where(iid: identifier).first
-        elsif project.jira_tracker?
-          JiraIssue.new(identifier, project)
-        end
-      end.compact.uniq
+      # TODO (rspeicher): What about external issues?
+      #EE code
+#<<<<<<< HEAD
+      #references[:issue].uniq.map do |project, identifier|
+        #if project.default_issues_tracker?
+          #project.issues.where(iid: identifier).first
+        #elsif project.jira_tracker?
+          #JiraIssue.new(identifier, project)
+        #end
+      #end.compact.uniq
+#=======
+
+      result = pipeline_result(:issue)
+      result.uniq
     end
 
     def merge_requests
-      references[:merge_request].uniq.map do |project, identifier|
-        project.merge_requests.where(iid: identifier).first
-      end.compact.uniq
+      result = pipeline_result(:merge_request)
+      result.uniq
     end
 
     def snippets
-      references[:snippet].uniq.map do |project, identifier|
-        project.snippets.where(id: identifier).first
-      end.compact.uniq
+      result = pipeline_result(:snippet)
+      result.uniq
     end
 
     def commits
-      references[:commit].uniq.map do |project, identifier|
-        repo = project.repository
-        repo.commit(identifier) if repo
-      end.compact.uniq
+      result = pipeline_result(:commit)
+      result.uniq
     end
 
     def commit_ranges
-      references[:commit_range].uniq.map do |project, identifier|
-        repo = project.repository
-        if repo
-          from_id, to_id = identifier.split(/\.{2,3}/, 2)
-          [repo.commit(from_id), repo.commit(to_id)]
-        end
-      end.compact.uniq
+      result = pipeline_result(:commit_range)
+      result.uniq
     end
 
     private
 
-    NAME_STR = Gitlab::Regex::NAMESPACE_REGEX_STR
-    PROJ_STR = "(?<project>#{NAME_STR}/#{NAME_STR})"
-
-    REFERENCE_PATTERN = %r{
-      (?<prefix>\W)?                         # Prefix
-      (                                      # Reference
-         @(?<user>#{NAME_STR})               # User name
-        |~(?<label>\d+)                      # Label ID
-        |(?<issue>([A-Z\-]+-)\d+)            # JIRA Issue ID
-        |#{PROJ_STR}?\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
-        |#{PROJ_STR}?!(?<merge_request>\d+)  # MR ID
-        |\$(?<snippet>\d+)                   # Snippet ID
-        |(#{PROJ_STR}@)?(?<commit_range>[\h]{6,40}\.{2,3}[\h]{6,40}) # Commit range
-        |(#{PROJ_STR}@)?(?<commit>[\h]{6,40}) # Commit ID
-      )
-      (?<suffix>\W)?                         # Suffix
-    }x.freeze
-
-    TYPES = %i(user issue label merge_request snippet commit commit_range).freeze
-
-    def parse_references(text, project = @project)
-      # parse reference links
-      text.gsub!(REFERENCE_PATTERN) do |match|
-        type = TYPES.detect { |t| $~[t].present? }
-
-        actual_project = project
-        project_prefix = nil
-        project_path = $LAST_MATCH_INFO[:project]
-        if project_path
-          actual_project = ::Project.find_with_namespace(project_path)
-          actual_project = nil unless can?(current_user, :read_project, actual_project)
-          project_prefix = project_path
-        end
-
-        parse_result($LAST_MATCH_INFO, type,
-                     actual_project, project_prefix) || match
-      end
-    end
-
-    # Called from #parse_references.  Attempts to build a gitlab reference
-    # link.  Returns nil if +type+ is nil, if the match string is an HTML
-    # entity, if the reference is invalid, or if the matched text includes an
-    # invalid project path.
-    def parse_result(match_info, type, project, project_prefix)
-      prefix = match_info[:prefix]
-      suffix = match_info[:suffix]
-
-      return nil if html_entity?(prefix, suffix) || type.nil?
-      return nil if project.nil? && !project_prefix.nil?
-
-      identifier = match_info[type]
-      ref_link = reference_link(type, identifier, project, project_prefix)
-
-      if ref_link
-        "#{prefix}#{ref_link}#{suffix}"
-      else
-        nil
-      end
-    end
-
-    # Return true if the +prefix+ and +suffix+ indicate that the matched string
-    # is an HTML entity like &amp;
-    def html_entity?(prefix, suffix)
-      prefix && suffix && prefix[0] == '&' && suffix[-1] == ';'
-    end
-
-    def reference_link(type, identifier, project, _)
-      references[type] << [project, identifier]
+    # Instantiate and call HTML::Pipeline with a single reference filter type,
+    # returning the result
+    #
+    # filter_type - Symbol reference type (e.g., :commit, :issue, etc.)
+    #
+    # Returns the results Array for the requested filter type
+    def pipeline_result(filter_type)
+      klass  = filter_type.to_s.camelize + 'ReferenceFilter'
+      filter = "Gitlab::Markdown::#{klass}".constantize
+
+      context = {
+        project: project,
+        current_user: current_user,
+        # We don't actually care about the links generated
+        only_path: true
+      }
+
+      pipeline = HTML::Pipeline.new([filter], context)
+      result = pipeline.call(@_text)
+
+      result[:references][filter_type]
     end
   end
 end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 9aeed5e69399c27cd0e88362d194029042090bae..0571574aa4fdc5e538835b398046e4723c5cfe34 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -2,7 +2,7 @@ module Gitlab
   module Regex
     extend self
 
-    NAMESPACE_REGEX_STR = '(?:[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.]*)'.freeze
+    NAMESPACE_REGEX_STR = '(?:[a-zA-Z0-9_\.][a-zA-Z0-9_\-\.]*[a-zA-Z0-9_\-]|[a-zA-Z0-9_])'.freeze
 
     def namespace_regex
       @namespace_regex ||= /\A#{NAMESPACE_REGEX_STR}\z/.freeze
@@ -10,7 +10,7 @@ def namespace_regex
 
     def namespace_regex_message
       "can contain only letters, digits, '_', '-' and '.'. " \
-      "Cannot start with '-'." \
+      "Cannot start with '-' or end in '.'." \
     end
 
 
diff --git a/lib/gitlab/theme.rb b/lib/gitlab/theme.rb
index 43093c7d27ebbf804adef24176611b0a31ed6075..f0e61aa2e81ad6b8c94652f7b8b55c814e04fc48 100644
--- a/lib/gitlab/theme.rb
+++ b/lib/gitlab/theme.rb
@@ -7,33 +7,44 @@ class Theme
     COLOR  = 5 unless const_defined?(:COLOR)
     BLUE   = 6 unless const_defined?(:BLUE)
 
-    def self.css_class_by_id(id)
-      themes = {
-        BASIC  => "ui_basic",
-        MARS   => "ui_mars",
-        MODERN => "ui_modern",
-        GRAY   => "ui_gray",
-        COLOR  => "ui_color",
-        BLUE   => "ui_blue"
+    def self.classes
+      @classes ||= {
+        BASIC  => 'ui_basic',
+        MARS   => 'ui_mars',
+        MODERN => 'ui_modern',
+        GRAY   => 'ui_gray',
+        COLOR  => 'ui_color',
+        BLUE   => 'ui_blue'
       }
+    end
 
+    def self.css_class_by_id(id)
       id ||= Gitlab.config.gitlab.default_theme
-
-      themes[id]
+      classes[id]
     end
 
-    def self.type_css_class_by_id(id)
-      types = {
+    def self.types
+      @types ||= {
         BASIC  => 'light_theme',
         MARS   => 'dark_theme',
         MODERN => 'dark_theme',
         GRAY   => 'dark_theme',
-        COLOR  => 'dark_theme'
+        COLOR  => 'dark_theme',
+        BLUE   => 'light_theme'
       }
+    end
 
+    def self.type_css_class_by_id(id)
       id ||= Gitlab.config.gitlab.default_theme
-
       types[id]
     end
+
+    # Convenience method to get a space-separated String of all the theme
+    # classes that mighty be applied to the `body` element
+    #
+    # Returns a String
+    def self.body_classes
+      (classes.values + types.values).uniq.join(' ')
+    end
   end
 end
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 04a2eb12db0f5a8a6c3e1060814f414e20e57c7d..1a6303b6c8252285826cf8f829f3b373d14e21c5 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -282,7 +282,8 @@ namespace :gitlab do
     def check_redis_version
       print "Redis version >= 2.0.0? ... "
 
-      if run_and_match(%W(redis-cli --version), /redis-cli 2.\d.\d/)
+      redis_version = run(%W(redis-cli --version))
+      if redis_version.try(:match, /redis-cli 2.\d.\d/) || redis_version.try(:match, /redis-cli 3.\d.\d/)
         puts "yes".green
       else
         puts "no".red
diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb
index 3394a1f863fa1f12477a2ea0466c186e14120a12..2cfa399a047ac0de4161a429f6479f22799dc343 100644
--- a/spec/controllers/commit_controller_spec.rb
+++ b/spec/controllers/commit_controller_spec.rb
@@ -3,7 +3,7 @@
 describe Projects::CommitController do
   let(:project) { create(:project) }
   let(:user)    { create(:user) }
-  let(:commit)  { project.repository.commit("master") }
+  let(:commit)  { project.commit("master") }
 
   before do
     sign_in(user)
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb
index fca1a06eb88d3d0ec2dd397a5e95cf9aec723ef6..133beba7b98e96f96b678a2b26aafb97a37932b7 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/gitlab_flavored_markdown_spec.rb
@@ -14,7 +14,7 @@
     Commit.any_instance.stub(title: "fix ##{issue.iid}\n\nask @#{fred.username} for details")
   end
 
-  let(:commit) { project.repository.commit }
+  let(:commit) { project.commit }
 
   before do
     login_as :user
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index e5f33d5a25acba8971b86f6cdbe7240875a43898..e217f0739d2919d07e8163a4cd1f7b2aa9448c1c 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -21,7 +21,7 @@
     end
 
     before do
-      visit namespace_project_issues_path(project.namespace, project)
+      visit edit_namespace_project_issue_path(project.namespace, project, issue)
       click_link "Edit"
     end
 
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index 5bd09793b11dac1209da4d095263dc0b728d1181..95719b4b49f474c70941cc381baa8f9be48923d1 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -4,7 +4,7 @@
   include RepoHelpers
 
   let(:project) { create(:project) }
-  let(:commit) { project.repository.commit(sample_commit.id) }
+  let(:commit) { project.commit(sample_commit.id) }
   let(:diff) { commit.diffs.first }
   let(:diff_file) { Gitlab::Diff::File.new(diff) }
 
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 64f130e4ae48b26d65c68552a345031f45194248..e309dbb6a2f9a6429b8b35aaec9de3ac6802f7b2 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -6,7 +6,7 @@
   let!(:project) { create(:project) }
 
   let(:user)          { create(:user, username: 'gfm') }
-  let(:commit)        { project.repository.commit }
+  let(:commit)        { project.commit }
   let(:issue)         { create(:issue, project: project) }
   let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
   let(:snippet)       { create(:project_snippet, project: project) }
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index 8271e00f41b24f03669224eaf86cc7d2653efa71..2013b3e4c2a84160ecff0385f4944acb3dee52e1 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -6,7 +6,7 @@
 
     before {
       @repository = project.repository
-      @commit = project.repository.commit("e56497bb")
+      @commit = project.commit("e56497bb")
     }
 
     context "on a directory containing more than one file/directory" do
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3840e64981fabada491d16dd5152091464c3b2e2
--- /dev/null
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe VisibilityLevelHelper do
+  include Haml::Helpers
+
+  before :all do
+    init_haml_helpers
+  end
+
+  let(:project) { create(:project) }
+
+  describe 'visibility_level_description' do
+    shared_examples 'a visibility level description' do
+      let(:desc) do
+        visibility_level_description(Gitlab::VisibilityLevel::PRIVATE,
+                                     form_model)
+      end
+
+      let(:expected_class) do
+        class_name = case form_model.class.name
+                     when 'String'
+                       form_model
+                     else
+                       form_model.class.name
+                     end
+
+        class_name.match(/(project|snippet)$/i)[0]
+      end
+
+      it 'should refer to the correct class' do
+        expect(desc).to match(/#{expected_class}/i)
+      end
+    end
+
+    context 'form_model argument is a String' do
+      context 'model object is a personal snippet' do
+        it_behaves_like 'a visibility level description' do
+          let(:form_model) { 'PersonalSnippet' }
+        end
+      end
+
+      context 'model object is a project snippet' do
+        it_behaves_like 'a visibility level description' do
+          let(:form_model) { 'ProjectSnippet' }
+        end
+      end
+
+      context 'model object is a project' do
+        it_behaves_like 'a visibility level description' do
+          let(:form_model) { 'Project' }
+        end
+      end
+    end
+
+    context 'form_model argument is a model object' do
+      context 'model object is a personal snippet' do
+        it_behaves_like 'a visibility level description' do
+          let(:form_model) { create(:personal_snippet) }
+        end
+      end
+
+      context 'model object is a project snippet' do
+        it_behaves_like 'a visibility level description' do
+          let(:form_model) { create(:project_snippet, project: project) }
+        end
+      end
+
+      context 'model object is a project' do
+        it_behaves_like 'a visibility level description' do
+          let(:form_model) { project }
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 40eb45e37cafd9348b73200e5893312c01889672..8b7946f3117269ec0edefea673971b5473f39985 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -4,7 +4,7 @@
   include RepoHelpers
 
   let(:project) { create(:project) }
-  let(:commit) { project.repository.commit(sample_commit.id) }
+  let(:commit) { project.commit(sample_commit.id) }
   let(:diff) { commit.diffs.first }
   let(:diff_file) { Gitlab::Diff::File.new(diff) }
 
diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb
index 918f6d0ead4f11b408d30f4fb06918558cdcdd30..4d5d1431683f8995c348494f8b180d77a4429a01 100644
--- a/spec/lib/gitlab/diff/parser_spec.rb
+++ b/spec/lib/gitlab/diff/parser_spec.rb
@@ -4,7 +4,7 @@
   include RepoHelpers
 
   let(:project) { create(:project) }
-  let(:commit) { project.repository.commit(sample_commit.id) }
+  let(:commit) { project.commit(sample_commit.id) }
   let(:diff) { commit.diffs.first }
   let(:parser) { Gitlab::Diff::Parser.new }
 
diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
index 5ebdc8926e203b9c2698046a160a89426bff6550..7274cb309a0a5fb9cced19f7d463cd7de3df5b4a 100644
--- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
@@ -5,8 +5,8 @@ module Gitlab::Markdown
     include ReferenceFilterSpecHelper
 
     let(:project) { create(:project) }
-    let(:commit1) { project.repository.commit }
-    let(:commit2) { project.repository.commit("HEAD~2") }
+    let(:commit1) { project.commit }
+    let(:commit2) { project.commit("HEAD~2") }
 
     it 'requires project context' do
       expect { described_class.call('Commit Range 1c002d..d200c1', {}) }.
@@ -42,13 +42,17 @@ module Gitlab::Markdown
         reference = "#{commit1.short_id}...#{commit2.id}"
         reference2 = "#{commit1.id}...#{commit2.short_id}"
 
-        expect(filter("See #{reference}").css('a').first.text).to eq reference
-        expect(filter("See #{reference2}").css('a').first.text).to eq reference2
+        exp = commit1.short_id + '...' + commit2.short_id
+
+        expect(filter("See #{reference}").css('a').first.text).to eq exp
+        expect(filter("See #{reference2}").css('a').first.text).to eq exp
       end
 
       it 'links with adjacent text' do
         doc = filter("See (#{reference}.)")
-        expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+
+        exp = Regexp.escape("#{commit1.short_id}...#{commit2.short_id}")
+        expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
       end
 
       it 'ignores invalid commit IDs' do
@@ -81,13 +85,18 @@ module Gitlab::Markdown
         expect(link).not_to match %r(https?://)
         expect(link).to eq urls.namespace_project_compare_url(project.namespace, project, from: commit1.id, to: commit2.id, only_path: true)
       end
+
+      it 'adds to the results hash' do
+        result = pipeline_result("See #{reference}")
+        expect(result[:references][:commit_range]).not_to be_empty
+      end
     end
 
     context 'cross-project reference' do
       let(:namespace) { create(:namespace, name: 'cross-reference') }
       let(:project2)  { create(:project, namespace: namespace) }
-      let(:commit1)   { project.repository.commit }
-      let(:commit2)   { project.repository.commit("HEAD~2") }
+      let(:commit1)   { project.commit }
+      let(:commit2)   { project.commit("HEAD~2") }
       let(:reference) { "#{project2.path_with_namespace}@#{commit1.id}...#{commit2.id}" }
 
       context 'when user can access reference' do
@@ -102,7 +111,9 @@ module Gitlab::Markdown
 
         it 'links with adjacent text' do
           doc = filter("Fixed (#{reference}.)")
-          expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+
+          exp = Regexp.escape("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
+          expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
         end
 
         it 'ignores invalid commit IDs on the referenced project' do
@@ -112,6 +123,11 @@ module Gitlab::Markdown
           exp = act = "Fixed #{project2.path_with_namespace}##{commit1.id}...#{commit2.id.reverse}"
           expect(filter(act).to_html).to eq exp
         end
+
+        it 'adds to the results hash' do
+          result = pipeline_result("See #{reference}")
+          expect(result[:references][:commit_range]).not_to be_empty
+        end
       end
 
       context 'when user cannot access reference' do
diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
index 71fd2db2c5899e8722120f26ae2cef74b6a4ceee..cc32a4fcf030f6fb6bef269d7587a3be150bd998 100644
--- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
@@ -5,7 +5,7 @@ module Gitlab::Markdown
     include ReferenceFilterSpecHelper
 
     let(:project) { create(:project) }
-    let(:commit)  { project.repository.commit }
+    let(:commit)  { project.commit }
 
     it 'requires project context' do
       expect { described_class.call('Commit 1c002d', {}) }.
@@ -27,15 +27,23 @@ module Gitlab::Markdown
         it "links to a valid reference of #{size} characters" do
           doc = filter("See #{reference[0...size]}")
 
-          expect(doc.css('a').first.text).to eq reference[0...size]
+          expect(doc.css('a').first.text).to eq commit.short_id
           expect(doc.css('a').first.attr('href')).
             to eq urls.namespace_project_commit_url(project.namespace, project, reference)
         end
       end
 
+      it 'always uses the short ID as the link text' do
+        doc = filter("See #{commit.id}")
+        expect(doc.text).to eq "See #{commit.short_id}"
+
+        doc = filter("See #{commit.id[0...6]}")
+        expect(doc.text).to eq "See #{commit.short_id}"
+      end
+
       it 'links with adjacent text' do
         doc = filter("See (#{reference}.)")
-        expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+        expect(doc.to_html).to match(/\(<a.+>#{commit.short_id}<\/a>\.\)/)
       end
 
       it 'ignores invalid commit IDs' do
@@ -55,7 +63,7 @@ module Gitlab::Markdown
         allow_any_instance_of(Commit).to receive(:title).and_return(%{"></a>whatever<a title="})
 
         doc = filter("See #{reference}")
-        expect(doc.text).to eq "See #{commit.id}"
+        expect(doc.text).to eq "See #{commit.short_id}"
       end
 
       it 'includes default classes' do
@@ -75,12 +83,17 @@ module Gitlab::Markdown
         expect(link).not_to match %r(https?://)
         expect(link).to eq urls.namespace_project_commit_url(project.namespace, project, reference, only_path: true)
       end
+
+      it 'adds to the results hash' do
+        result = pipeline_result("See #{reference}")
+        expect(result[:references][:commit]).not_to be_empty
+      end
     end
 
     context 'cross-project reference' do
       let(:namespace) { create(:namespace, name: 'cross-reference') }
       let(:project2)  { create(:project, namespace: namespace) }
-      let(:commit)    { project.repository.commit }
+      let(:commit)    { project.commit }
       let(:reference) { "#{project2.path_with_namespace}@#{commit.id}" }
 
       context 'when user can access reference' do
@@ -95,13 +108,20 @@ module Gitlab::Markdown
 
         it 'links with adjacent text' do
           doc = filter("Fixed (#{reference}.)")
-          expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
+
+          exp = Regexp.escape(project2.path_with_namespace)
+          expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/)
         end
 
         it 'ignores invalid commit IDs on the referenced project' do
           exp = act = "Committed #{project2.path_with_namespace}##{commit.id.reverse}"
           expect(filter(act).to_html).to eq exp
         end
+
+        it 'adds to the results hash' do
+          result = pipeline_result("See #{reference}")
+          expect(result[:references][:commit]).not_to be_empty
+        end
       end
 
       context 'when user cannot access reference' do
diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
index f95b37d69544422117aaadc35e2b50111d1ca6be..393bf32e19610c632fd310b53af2e8f4e7920902 100644
--- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
@@ -34,7 +34,7 @@ def helper
       end
 
       it 'links to a valid reference' do
-        doc = filter("See #{reference}")
+        doc = filter("Fixed #{reference}")
 
         expect(doc.css('a').first.attr('href')).
           to eq helper.url_for_issue(issue.iid, project)
@@ -81,6 +81,11 @@ def helper
         expect(link).not_to match %r(https?://)
         expect(link).to eq helper.url_for_issue(issue.iid, project, only_path: true)
       end
+
+      it 'adds to the results hash' do
+        result = pipeline_result("Fixed #{reference}")
+        expect(result[:references][:issue]).to eq [issue]
+      end
     end
 
     context 'cross-project reference' do
@@ -117,6 +122,11 @@ def helper
 
           expect(filter(act).to_html).to eq exp
         end
+
+        it 'adds to the results hash' do
+          result = pipeline_result("Fixed #{reference}")
+          expect(result[:references][:issue]).to eq [issue]
+        end
       end
 
       context 'when user cannot access reference' do
diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
index c84e568e17269417d42fa28421c0f1f368f7934c..9f898837466c88cc7e7bff38c2fb23e563d2a289 100644
--- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
@@ -39,6 +39,11 @@ module Gitlab::Markdown
       expect(link).to eq urls.namespace_project_issues_url(project.namespace, project, label_name: label.name, only_path: true)
     end
 
+    it 'adds to the results hash' do
+      result = pipeline_result("Label #{reference}")
+      expect(result[:references][:label]).to eq [label]
+    end
+
     describe 'label span element' do
       it 'includes default classes' do
         doc = filter("Label #{reference}")
diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
index 0f66442269b7dba839a6f0add9005ababbb5981d..d6e745114f288aa63f5dad883a74924286014b8b 100644
--- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
@@ -69,6 +69,11 @@ module Gitlab::Markdown
         expect(link).not_to match %r(https?://)
         expect(link).to eq urls.namespace_project_merge_request_url(project.namespace, project, merge, only_path: true)
       end
+
+      it 'adds to the results hash' do
+        result = pipeline_result("Merge #{reference}")
+        expect(result[:references][:merge_request]).to eq [merge]
+      end
     end
 
     context 'cross-project reference' do
@@ -98,6 +103,11 @@ module Gitlab::Markdown
 
           expect(filter(act).to_html).to eq exp
         end
+
+        it 'adds to the results hash' do
+          result = pipeline_result("Merge #{reference}")
+          expect(result[:references][:merge_request]).to eq [merge]
+        end
       end
 
       context 'when user cannot access reference' do
diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
index 79533a90b558d2e8f7797252d632e61530d8e98f..a4b331157afe5a8d9ef229eb5ad60d8cbd3bebbf 100644
--- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
@@ -68,6 +68,11 @@ module Gitlab::Markdown
         expect(link).not_to match %r(https?://)
         expect(link).to eq urls.namespace_project_snippet_url(project.namespace, project, snippet, only_path: true)
       end
+
+      it 'adds to the results hash' do
+        result = pipeline_result("Snippet #{reference}")
+        expect(result[:references][:snippet]).to eq [snippet]
+      end
     end
 
     context 'cross-project reference' do
@@ -96,6 +101,11 @@ module Gitlab::Markdown
 
           expect(filter(act).to_html).to eq exp
         end
+
+        it 'adds to the results hash' do
+          result = pipeline_result("Snippet #{reference}")
+          expect(result[:references][:snippet]).to eq [snippet]
+        end
       end
 
       context 'when user cannot access reference' do
diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
index a5eb927072e5f5a79baf989efdfaf7455f7eb400..922502ada334f92f75c43b4ad3499445a633c625 100644
--- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
@@ -24,9 +24,29 @@ module Gitlab::Markdown
       end
     end
 
+    context 'mentioning @all' do
+      before do
+        project.team << [project.creator, :developer]
+      end
+
+      it 'supports a special @all mention' do
+        doc = filter("Hey @all")
+        expect(doc.css('a').length).to eq 1
+        expect(doc.css('a').first.attr('href'))
+          .to eq urls.namespace_project_url(project.namespace, project)
+      end
+
+      it 'adds to the results hash' do
+        result = pipeline_result('Hey @all')
+        expect(result[:references][:user]).to eq [project.creator]
+      end
+    end
+
     context 'mentioning a user' do
+      let(:reference) { "@#{user.username}" }
+
       it 'links to a User' do
-        doc = filter("Hey @#{user.username}")
+        doc = filter("Hey #{reference}")
         expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
       end
 
@@ -45,22 +65,45 @@ module Gitlab::Markdown
         doc = filter("Hey @#{user.username}")
         expect(doc.css('a').length).to eq 1
       end
+
+      it 'adds to the results hash' do
+        result = pipeline_result("Hey #{reference}")
+        expect(result[:references][:user]).to eq [user]
+      end
     end
 
     context 'mentioning a group' do
       let(:group) { create(:group) }
       let(:user)  { create(:user) }
 
-      it 'links to a Group that the current user can read' do
-        group.add_user(user, Gitlab::Access::DEVELOPER)
+      let(:reference) { "@#{group.name}" }
+
+      context 'that the current user can read' do
+        before do
+          group.add_user(user, Gitlab::Access::DEVELOPER)
+        end
 
-        doc = filter("Hey @#{group.name}", current_user: user)
-        expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
+        it 'links to the Group' do
+          doc = filter("Hey #{reference}", current_user: user)
+          expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
+        end
+
+        it 'adds to the results hash' do
+          result = pipeline_result("Hey #{reference}", current_user: user)
+          expect(result[:references][:user]).to eq group.users
+        end
       end
 
-      it 'ignores references to a Group that the current user cannot read' do
-        doc = filter("Hey @#{group.name}", current_user: user)
-        expect(doc.to_html).to eq "Hey @#{group.name}"
+      context 'that the current user cannot read' do
+        it 'ignores references to the Group' do
+          doc = filter("Hey #{reference}", current_user: user)
+          expect(doc.to_html).to eq "Hey #{reference}"
+        end
+
+        it 'does not add to the results hash' do
+          result = pipeline_result("Hey #{reference}", current_user: user)
+          expect(result[:references][:user]).to eq []
+        end
       end
     end
 
@@ -70,13 +113,6 @@ module Gitlab::Markdown
       expect(doc.to_html).to match(/\(<a.+>@#{user.username}<\/a>\.\)/)
     end
 
-    it 'supports a special @all mention' do
-      doc = filter("Hey @all")
-      expect(doc.css('a').length).to eq 1
-      expect(doc.css('a').first.attr('href'))
-        .to eq urls.namespace_project_url(project.namespace, project)
-    end
-
     it 'includes default classes' do
       doc = filter("Hey @#{user.username}")
       expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member'
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index a33b5a6d22088e8e31c49d253dec203b5bfc7c36..77afa7d1ebe0c47fdbbfb267699804bf9976e31f 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -4,80 +4,6 @@
   let(:project) { create(:project) }
   subject { Gitlab::ReferenceExtractor.new(project, project.creator) }
 
-  it 'extracts username references' do
-    subject.analyze('this contains a @user reference')
-    expect(subject.references[:user]).to eq([[project, 'user']])
-  end
-
-  it 'extracts issue references' do
-    subject.analyze('this one talks about issue #1234')
-    expect(subject.references[:issue]).to eq([[project, '1234']])
-  end
-
-  it 'extracts JIRA issue references' do
-    subject.analyze('this one talks about issue JIRA-1234')
-    expect(subject.references[:issue]).to eq([[project, 'JIRA-1234']])
-  end
-
-  it 'extracts merge request references' do
-    subject.analyze("and here's !43, a merge request")
-    expect(subject.references[:merge_request]).to eq([[project, '43']])
-  end
-
-  it 'extracts snippet ids' do
-    subject.analyze('snippets like $12 get extracted as well')
-    expect(subject.references[:snippet]).to eq([[project, '12']])
-  end
-
-  it 'extracts commit shas' do
-    subject.analyze('commit shas 98cf0ae3 are pulled out as Strings')
-    expect(subject.references[:commit]).to eq([[project, '98cf0ae3']])
-  end
-
-  it 'extracts commit ranges' do
-    subject.analyze('here you go, a commit range: 98cf0ae3...98cf0ae4')
-    expect(subject.references[:commit_range]).to eq([[project, '98cf0ae3...98cf0ae4']])
-  end
-
-  it 'extracts multiple references and preserves their order' do
-    subject.analyze('@me and @you both care about this')
-    expect(subject.references[:user]).to eq([
-      [project, 'me'],
-      [project, 'you']
-    ])
-  end
-
-  it 'leaves the original note unmodified' do
-    text = 'issue #123 is just the worst, @user'
-    subject.analyze(text)
-    expect(text).to eq('issue #123 is just the worst, @user')
-  end
-
-  it 'extracts no references for <pre>..</pre> blocks' do
-    subject.analyze("<pre>def puts '#1 issue'\nend\n</pre>```")
-    expect(subject.issues).to be_blank
-  end
-
-  it 'extracts no references for <code>..</code> blocks' do
-    subject.analyze("<code>def puts '!1 request'\nend\n</code>```")
-    expect(subject.merge_requests).to be_blank
-  end
-
-  it 'extracts no references for code blocks with language' do
-    subject.analyze("this code:\n```ruby\ndef puts '#1 issue'\nend\n```")
-    expect(subject.issues).to be_blank
-  end
-
-  it 'extracts issue references for invalid code blocks' do
-    subject.analyze('test: ```this one talks about issue #1234```')
-    expect(subject.references[:issue]).to eq([[project, '1234']])
-  end
-
-  it 'handles all possible kinds of references' do
-    accessors = described_class::TYPES.map { |t| "#{t}s".to_sym }
-    expect(subject).to respond_to(*accessors)
-  end
-
   it 'accesses valid user objects' do
     @u_foo = create(:user, username: 'foo')
     @u_bar = create(:user, username: 'bar')
@@ -135,7 +61,7 @@
   end
 
   it 'accesses valid commits' do
-    commit = project.repository.commit('master')
+    commit = project.commit('master')
 
     subject.analyze("this references commits #{commit.sha[0..6]} and 012345")
     extracted = subject.commits
@@ -145,16 +71,16 @@
   end
 
   it 'accesses valid commit ranges' do
-    commit = project.repository.commit('master')
-    earlier_commit = project.repository.commit('master~2')
+    commit = project.commit('master')
+    earlier_commit = project.commit('master~2')
 
     subject.analyze("this references commits #{earlier_commit.sha[0..6]}...#{commit.sha[0..6]}")
+
     extracted = subject.commit_ranges
     expect(extracted.size).to eq(1)
-    expect(extracted[0][0].sha).to eq(earlier_commit.sha)
-    expect(extracted[0][0].message).to eq(earlier_commit.message)
-    expect(extracted[0][1].sha).to eq(commit.sha)
-    expect(extracted[0][1].message).to eq(commit.message)
+    expect(extracted.first).to be_kind_of(CommitRange)
+    expect(extracted.first.commit_from).to eq earlier_commit
+    expect(extracted.first.commit_to).to eq commit
   end
 
   context 'with a project with an underscore' do
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index e4900e4761a1a0530718c4cdf935d4210623783b..faa7ea7abe37bdd0ca95f0ba7bb8fdd8d328453b 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -466,7 +466,7 @@
       end
 
       describe 'on a commit' do
-        let(:commit) { project.repository.commit }
+        let(:commit) { project.commit }
 
         before(:each) { allow(note).to receive(:noteable).and_return(commit) }
 
@@ -670,8 +670,8 @@
     let(:example_site_path) { root_path }
     let(:user) { create(:user) }
     let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_image_commit.id, sample_commit.id) }
-    let(:commits) { Commit.decorate(compare.commits) }
-    let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base), to: Commit.new(compare.head)) }
+    let(:commits) { Commit.decorate(compare.commits, nil) }
+    let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base, project), to: Commit.new(compare.head, project)) }
     let(:send_from_committer_email) { false }
 
     subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, send_from_committer_email: send_from_committer_email) }
@@ -774,7 +774,7 @@
     let(:example_site_path) { root_path }
     let(:user) { create(:user) }
     let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_commit.parent_id, sample_commit.id) }
-    let(:commits) { Commit.decorate(compare.commits) }
+    let(:commits) { Commit.decorate(compare.commits, nil) }
     let(:diff_path) { namespace_project_commit_path(project.namespace, project, commits.first) }
 
     subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) }
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..31ee3e99cad6eccbecb79e94406571944ffcf6b0
--- /dev/null
+++ b/spec/models/commit_range_spec.rb
@@ -0,0 +1,120 @@
+require 'spec_helper'
+
+describe CommitRange do
+  let(:sha_from) { 'f3f85602' }
+  let(:sha_to)   { 'e86e1013' }
+
+  let(:range)  { described_class.new("#{sha_from}...#{sha_to}") }
+  let(:range2) { described_class.new("#{sha_from}..#{sha_to}") }
+
+  it 'raises ArgumentError when given an invalid range string' do
+    expect { described_class.new("Foo") }.to raise_error
+  end
+
+  describe '#to_s' do
+    it 'is correct for three-dot syntax' do
+      expect(range.to_s).to eq "#{sha_from[0..7]}...#{sha_to[0..7]}"
+    end
+
+    it 'is correct for two-dot syntax' do
+      expect(range2.to_s).to eq "#{sha_from[0..7]}..#{sha_to[0..7]}"
+    end
+  end
+
+  describe '#reference_title' do
+    it 'returns the correct String for three-dot ranges' do
+      expect(range.reference_title).to eq "Commits #{sha_from} through #{sha_to}"
+    end
+
+    it 'returns the correct String for two-dot ranges' do
+      expect(range2.reference_title).to eq "Commits #{sha_from}^ through #{sha_to}"
+    end
+  end
+
+  describe '#to_param' do
+    it 'includes the correct keys' do
+      expect(range.to_param.keys).to eq %i(from to)
+    end
+
+    it 'includes the correct values for a three-dot range' do
+      expect(range.to_param).to eq({from: sha_from, to: sha_to})
+    end
+
+    it 'includes the correct values for a two-dot range' do
+      expect(range2.to_param).to eq({from: sha_from + '^', to: sha_to})
+    end
+  end
+
+  describe '#exclude_start?' do
+    it 'is false for three-dot ranges' do
+      expect(range.exclude_start?).to eq false
+    end
+
+    it 'is true for two-dot ranges' do
+      expect(range2.exclude_start?).to eq true
+    end
+  end
+
+  describe '#valid_commits?' do
+    context 'without a project' do
+      it 'returns nil' do
+        expect(range.valid_commits?).to be_nil
+      end
+    end
+
+    it 'accepts an optional project argument' do
+      project1 = double('project1').as_null_object
+      project2 = double('project2').as_null_object
+
+      # project1 gets assigned through the accessor, but ignored when not given
+      # as an argument to `valid_commits?`
+      expect(project1).not_to receive(:present?)
+      range.project = project1
+
+      # project2 gets passed to `valid_commits?`
+      expect(project2).to receive(:present?).and_return(false)
+
+      range.valid_commits?(project2)
+    end
+
+    context 'with a project' do
+      let(:project) { double('project', repository: double('repository')) }
+
+      context 'with a valid repo' do
+        before do
+          expect(project).to receive(:valid_repo?).and_return(true)
+          range.project = project
+        end
+
+        it 'is false when `sha_from` is invalid' do
+          expect(project.repository).to receive(:commit).with(sha_from).and_return(false)
+          expect(project.repository).not_to receive(:commit).with(sha_to)
+          expect(range).not_to be_valid_commits
+        end
+
+        it 'is false when `sha_to` is invalid' do
+          expect(project.repository).to receive(:commit).with(sha_from).and_return(true)
+          expect(project.repository).to receive(:commit).with(sha_to).and_return(false)
+          expect(range).not_to be_valid_commits
+        end
+
+        it 'is true when both `sha_from` and `sha_to` are valid' do
+          expect(project.repository).to receive(:commit).with(sha_from).and_return(true)
+          expect(project.repository).to receive(:commit).with(sha_to).and_return(true)
+          expect(range).to be_valid_commits
+        end
+      end
+
+      context 'without a valid repo' do
+        before do
+          expect(project).to receive(:valid_repo?).and_return(false)
+          range.project = project
+        end
+
+        it 'returns false' do
+          expect(range).not_to be_valid_commits
+        end
+      end
+    end
+  end
+end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 506bc3339b63d6f7ee3702d50d44881db0734bc4..ad2ac143d972c99e996d0e4475c79cde45423f1d 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -2,7 +2,7 @@
 
 describe Commit do
   let(:project) { create :project }
-  let(:commit) { project.repository.commit }
+  let(:commit) { project.commit }
 
   describe '#title' do
     it "returns no_commit_message when safe_message is blank" do
@@ -58,13 +58,13 @@
 
     it 'detects issues that this commit is marked as closing' do
       commit.stub(safe_message: "Fixes ##{issue.iid}")
-      expect(commit.closes_issues(project)).to eq([issue])
+      expect(commit.closes_issues).to eq([issue])
     end
 
     it 'does not detect issues from other projects' do
       ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}"
       commit.stub(safe_message: "Fixes #{ext_ref}")
-      expect(commit.closes_issues(project)).to be_empty
+      expect(commit.closes_issues).to be_empty
     end
   end
 
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index 67ec9193ad7db5daa111c11a1fbcfca249bd1567..14f873e6b5a924c643f1adc16e4cd73f789a3ba9 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -52,22 +52,26 @@
     end
 
     it "POSTs to the web hook URL" do
-      @project_hook.execute(@data)
-      expect(WebMock).to have_requested(:post, @project_hook.url).once
+      @project_hook.execute(@data, 'push_hooks')
+      expect(WebMock).to have_requested(:post, @project_hook.url).
+        with(headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook'}).
+        once
     end
 
     it "POSTs the data as JSON" do
       json = @data.to_json
 
-      @project_hook.execute(@data)
-      expect(WebMock).to have_requested(:post, @project_hook.url).with(body: json).once
+      @project_hook.execute(@data, 'push_hooks')
+      expect(WebMock).to have_requested(:post, @project_hook.url).
+        with(headers: {'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook'}).
+        once
     end
 
     it "catches exceptions" do
       expect(WebHook).to receive(:post).and_raise("Some HTTP Post error")
 
       expect {
-        @project_hook.execute(@data)
+        @project_hook.execute(@data, 'push_hooks')
       }.to raise_error
     end
   end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 96a6dea638c0bf52bf14a59e82257adae4691997..9800a20f8435df52f3c6a6b60b5e748cae7f9fb2 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -331,16 +331,17 @@
     let(:author)     { create(:user) }
     let(:issue)      { create(:issue, project: project) }
     let(:mergereq)   { create(:merge_request, :simple, target_project: project, source_project: project) }
-    let(:commit)     { project.repository.commit }
     let(:jira_issue) { JiraIssue.new("JIRA-1", project)}
     let(:jira_tracker) { project.create_jira_service if project.jira_service.nil? }
+    let(:commit)     { project.commit }
 
     # Test all of {issue, merge request, commit} in both the referenced and referencing
     # roles, to ensure that the correct information can be inferred from any argument.
 
     context 'issue from a merge request' do
+      subject { Note.create_cross_reference_note(issue, mergereq, author) }
+
       context 'in default issue tracker' do
-        subject { Note.create_cross_reference_note(issue, mergereq, author, project) }
 
         it { is_expected.to be_valid }
 
@@ -366,6 +367,7 @@
         end
       end
 
+
       context 'in JIRA issue tracker' do
         before do
           jira_service_settings
@@ -385,10 +387,9 @@
     end
 
     context 'issue from a commit' do
-      context 'in default issue tracker' do
-        subject { Note.create_cross_reference_note(issue, commit, author, project) }
-
+      subject { Note.create_cross_reference_note(issue, commit, author) }
 
+      context 'in default issue tracker' do
         it { is_expected.to be_valid }
 
         describe '#noteable' do
@@ -456,7 +457,7 @@
     end
 
     context 'merge request from an issue' do
-      subject { Note.create_cross_reference_note(mergereq, issue, author, project) }
+      subject { Note.create_cross_reference_note(mergereq, issue, author) }
 
       it { is_expected.to be_valid }
 
@@ -477,7 +478,7 @@
     end
 
     context 'commit from a merge request' do
-      subject { Note.create_cross_reference_note(commit, mergereq, author, project) }
+      subject { Note.create_cross_reference_note(commit, mergereq, author) }
 
       it { is_expected.to be_valid }
 
@@ -498,13 +499,13 @@
     end
 
     context 'commit contained in a merge request' do
-      subject { Note.create_cross_reference_note(mergereq.commits.first, mergereq, author, project) }
+      subject { Note.create_cross_reference_note(mergereq.commits.first, mergereq, author) }
 
       it { is_expected.to be_nil }
     end
 
     context 'commit from issue' do
-      subject { Note.create_cross_reference_note(commit, issue, author, project) }
+      subject { Note.create_cross_reference_note(commit, issue, author) }
 
       it { is_expected.to be_valid }
 
@@ -531,7 +532,7 @@
 
     context 'commit from commit' do
       let(:parent_commit) { commit.parents.first }
-      subject { Note.create_cross_reference_note(commit, parent_commit, author, project) }
+      subject { Note.create_cross_reference_note(commit, parent_commit, author) }
 
       it { is_expected.to be_valid }
 
@@ -561,11 +562,11 @@
     let(:project) { create :project }
     let(:author) { create :user }
     let(:issue) { create :issue }
-    let(:commit0) { project.repository.commit }
-    let(:commit1) { project.repository.commit('HEAD~2') }
+    let(:commit0) { project.commit }
+    let(:commit1) { project.commit('HEAD~2') }
 
     before do
-      Note.create_cross_reference_note(issue, commit0, author, project)
+      Note.create_cross_reference_note(issue, commit0, author)
     end
 
     it 'detects if a mentionable has already been mentioned' do
@@ -578,7 +579,7 @@
 
     context 'commit on commit' do
       before do
-        Note.create_cross_reference_note(commit0, commit1, author, project)
+        Note.create_cross_reference_note(commit0, commit1, author)
       end
 
       it { expect(Note.cross_reference_exists?(commit0, commit1)).to be_truthy }
@@ -606,7 +607,7 @@
     let(:author) { create :user }
     let(:issue0) { create :issue, project: project }
     let(:issue1) { create :issue, project: second_project }
-    let!(:note) { Note.create_cross_reference_note(issue0, issue1, author, project) }
+    let!(:note) { Note.create_cross_reference_note(issue0, issue1, author) }
 
     it 'detects if a mentionable has already been mentioned' do
       expect(Note.cross_reference_exists?(issue0, issue1)).to be_truthy
@@ -641,7 +642,7 @@
     end
 
     it 'should identify cross-reference notes as system notes' do
-      @note = Note.create_cross_reference_note(issue, other, author, project)
+      @note = Note.create_cross_reference_note(issue, other, author)
       expect(@note).to be_system
     end
 
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index 8ab847e64323f06c127ba3ecf8949590d86caff4..348f83c56ad5a20221d7b166e07d79a82a05fec5 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -213,5 +213,21 @@
             "<pre>snippet note</pre>")
       end
     end
+
+    context "#message_options" do
+      it "should be set to the defaults" do
+        expect(hipchat.send(:message_options)).to eq({notify: false, color: 'yellow'})
+      end
+
+      it "should set notfiy to true" do
+        hipchat.stub(notify: '1')
+        expect(hipchat.send(:message_options)).to eq({notify: true, color: 'yellow'})
+      end
+
+      it "should set the color" do
+        hipchat.stub(color: 'red')
+        expect(hipchat.send(:message_options)).to eq({notify: false, color: 'red'})
+      end
+    end
   end
 end
diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb
index fb3ff552c8d412440c4b4b6263d748cecb08ec77..7a784796031a63c3092e976f1e2e9a290f532675 100644
--- a/spec/requests/api/fork_spec.rb
+++ b/spec/requests/api/fork_spec.rb
@@ -50,7 +50,6 @@
       it 'should fail if forked project exists in the user namespace' do
         post api("/projects/fork/#{project.id}", user)
         expect(response.status).to eq(409)
-        expect(json_response['message']['base']).to eq(['Invalid fork destination'])
         expect(json_response['message']['name']).to eq(['has already been taken'])
         expect(json_response['message']['path']).to eq(['has already been taken'])
       end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index ba29498659a320ef5572149dd91914905dfc7811..f2939a4afc587a1f54fb3fc1290059a16ee13f83 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -45,7 +45,7 @@
     before do
       service.execute(project, user, @oldrev, @newrev, @ref)
       @push_data = service.push_data
-      @commit = project.repository.commit(@newrev)
+      @commit = project.commit(@newrev)
     end
 
     subject { @push_data }
@@ -152,7 +152,7 @@
   describe "cross-reference notes" do
     let(:issue) { create :issue, project: project }
     let(:commit_author) { create :user }
-    let(:commit) { project.repository.commit }
+    let(:commit) { project.commit }
 
     before do
       commit.stub({
@@ -165,22 +165,22 @@
     end
 
     it "creates a note if a pushed commit mentions an issue" do
-      expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
+      expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author)
 
       service.execute(project, user, @oldrev, @newrev, @ref)
     end
 
     it "only creates a cross-reference note if one doesn't already exist" do
-      Note.create_cross_reference_note(issue, commit, user, project)
+      Note.create_cross_reference_note(issue, commit, user)
 
-      expect(Note).not_to receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
+      expect(Note).not_to receive(:create_cross_reference_note).with(issue, commit, commit_author)
 
       service.execute(project, user, @oldrev, @newrev, @ref)
     end
 
     it "defaults to the pushing user if the commit's author is not known" do
       commit.stub(author_name: 'unknown name', author_email: 'unknown@email.com')
-      expect(Note).to receive(:create_cross_reference_note).with(issue, commit, user, project)
+      expect(Note).to receive(:create_cross_reference_note).with(issue, commit, user)
 
       service.execute(project, user, @oldrev, @newrev, @ref)
     end
@@ -189,7 +189,7 @@
       allow(project.repository).to receive(:commits_between).with(@blankrev, @newrev).and_return([])
       allow(project.repository).to receive(:commits_between).with("master", @newrev).and_return([commit])
 
-      expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author, project)
+      expect(Note).to receive(:create_cross_reference_note).with(issue, commit, commit_author)
 
       service.execute(project, user, @blankrev, @newrev, 'refs/heads/other')
     end
@@ -199,7 +199,7 @@
     let(:issue) { create :issue, project: project }
     let(:other_issue) { create :issue, project: project }
     let(:commit_author) { create :user }
-    let(:closing_commit) { project.repository.commit }
+    let(:closing_commit) { project.commit }
 
     context "for default gitlab issue tracker" do
       before do
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
index a050fdf6c0eb1ac58e08a7243c49d2c76c45db22..76f69b396e03efb3185f631333a9a3da7acedcc4 100644
--- a/spec/services/git_tag_push_service_spec.rb
+++ b/spec/services/git_tag_push_service_spec.rb
@@ -19,7 +19,7 @@
       @push_data = service.push_data
       @tag_name = Gitlab::Git.ref_name(@ref)
       @tag = project.repository.find_tag(@tag_name)
-      @commit = project.repository.commit(@tag.target)
+      @commit = project.commit(@tag.target)
     end
 
     subject { @push_data }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 6d2bc41c2b90342aaab01c096d626e6b67f1ca2f..2a54b2e920acdd6fc6bc5f25280bb0452d534d94 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -57,7 +57,7 @@
         end
 
         it 'filters out "mentioned in" notes' do
-          mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project)
+          mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author)
 
           expect(Notify).not_to receive(:note_issue_email)
           notification.new_note(mentioned_note)
@@ -128,7 +128,7 @@ def should_not_email(user_id)
         end
 
         it 'filters out "mentioned in" notes' do
-          mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author, issue.project)
+          mentioned_note = Note.create_cross_reference_note(mentioned_issue, issue, issue.author)
 
           expect(Notify).not_to receive(:note_issue_email)
           notification.new_note(mentioned_note)
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index c9025bdf133087619ec0999f79e543db7d51b97e..f158ac87e2b724eacbb1bd587cbd3600c0560fd7 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -27,7 +27,7 @@
       it "fails due to transaction failure" do
         @to_project = fork_project(@from_project, @to_user, false)
         expect(@to_project.errors).not_to be_empty
-        expect(@to_project.errors[:base]).to include("Fork transaction failed.")
+        expect(@to_project.errors[:base]).to include("Failed to fork repository")
       end
     end
 
@@ -36,8 +36,8 @@
         @existing_project = create(:project, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace)
         @to_project = fork_project(@from_project, @to_user)
         expect(@existing_project.persisted?).to be_truthy
-        expect(@to_project.errors[:base]).to include("Invalid fork destination")
-        expect(@to_project.errors[:base]).not_to include("Fork transaction failed.")
+        expect(@to_project.errors[:name]).to eq(['has already been taken'])
+        expect(@to_project.errors[:path]).to eq(['has already been taken'])
       end
     end
 
@@ -81,7 +81,7 @@
     context 'fork project for group when user not owner' do
       it 'group developer should fail to fork project into the group' do
         to_project = fork_project(@project, @developer, true, @opts)
-        expect(to_project.errors[:namespace]).to eq(['insufficient access rights'])
+        expect(to_project.errors[:namespace]).to eq(['is not valid'])
       end
     end
 
@@ -91,7 +91,6 @@
                                             namespace: @group)
         to_project = fork_project(@project, @group_owner, true, @opts)
         expect(existing_project.persisted?).to be_truthy
-        expect(to_project.errors[:base]).to eq(['Invalid fork destination'])
         expect(to_project.errors[:name]).to eq(['has already been taken'])
         expect(to_project.errors[:path]).to eq(['has already been taken'])
       end
@@ -99,10 +98,7 @@
   end
 
   def fork_project(from_project, user, fork_success = true, params = {})
-    context = Projects::ForkService.new(from_project, user, params)
-    shell = double('gitlab_shell')
-    shell.stub(fork_repository: fork_success)
-    context.stub(gitlab_shell: shell)
-    context.execute
+    allow_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_return(fork_success)
+    Projects::ForkService.new(from_project, user, params).execute
   end
 end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 53ccaa4fd6791833f2adcbaf028d579031bc2a39..8fe51cf4add38ef91311761efdd97b9dd94c73d0 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -10,19 +10,13 @@
 ENV["RAILS_ENV"] ||= 'test'
 require File.expand_path("../../config/environment", __FILE__)
 require 'rspec/rails'
-require 'capybara/rails'
-require 'capybara/rspec'
 require 'webmock/rspec'
 require 'email_spec'
 require 'sidekiq/testing/inline'
-require 'capybara/poltergeist'
-
-Capybara.javascript_driver = :poltergeist
-Capybara.default_wait_time = 10
 
 # Requires supporting ruby files with custom matchers and macros, etc,
 # in spec/support/ and its subdirectories.
-Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
+Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
 
 WebMock.disable_net_connect!(allow_localhost: true)
 
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fed1ab6ee33f743b181ab69ab8a74056b5020978
--- /dev/null
+++ b/spec/support/capybara.rb
@@ -0,0 +1,21 @@
+require 'capybara/rails'
+require 'capybara/rspec'
+require 'capybara/poltergeist'
+
+# Give CI some extra time
+timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 90 : 10
+
+Capybara.javascript_driver = :poltergeist
+Capybara.register_driver :poltergeist do |app|
+  Capybara::Poltergeist::Driver.new(app, js_errors: true, timeout: timeout)
+end
+
+Capybara.default_wait_time = timeout
+Capybara.ignore_hidden_elements = true
+
+unless ENV['CI'] || ENV['CI_SERVER']
+  require 'capybara-screenshot/rspec'
+
+  # Keep only the screenshots generated from the last failing test suite
+  Capybara::Screenshot.prune_strategy = :keep_last_run
+end
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index 25ec5cbe6f2a043d58af338897837e8af3500af0..53fb654555397953f1907fbf510207a31bbf9161 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -83,14 +83,14 @@ def common_mentionable_setup
 
     mentioned_objects.each do |referenced|
       expect(Note).to receive(:create_cross_reference_note).
-        with(referenced, subject.local_reference, author, project)
+        with(referenced, subject.local_reference, author)
     end
 
     subject.create_cross_references!(project, author)
   end
 
   it 'detects existing cross-references' do
-    Note.create_cross_reference_note(mentioned_issue, subject.local_reference, author, project)
+    Note.create_cross_reference_note(mentioned_issue, subject.local_reference, author)
 
     expect(subject).to have_mentioned(mentioned_issue)
     expect(subject).not_to have_mentioned(mentioned_mr)
@@ -107,6 +107,8 @@ def common_mentionable_setup
   end
 
   it 'creates new cross-reference notes when the mentionable text is edited' do
+    subject.save
+
     cross = ext_proj.path_with_namespace
 
     new_text = <<-MSG
@@ -132,10 +134,9 @@ def common_mentionable_setup
     # These two issues are new and should receive reference notes
     new_issues.each do |newref|
       expect(Note).to receive(:create_cross_reference_note).
-        with(newref, subject.local_reference, author, project)
+        with(newref, subject.local_reference, author)
     end
 
-    subject.save
     set_mentionable_text.call(new_text)
     subject.notice_added_references(project, author)
   end
diff --git a/spec/support/reference_filter_spec_helper.rb b/spec/support/reference_filter_spec_helper.rb
index bcee5715cad051658e518e7ff590799ee5598a50..41253811490e0872423b0c91bd3071d7736342f4 100644
--- a/spec/support/reference_filter_spec_helper.rb
+++ b/spec/support/reference_filter_spec_helper.rb
@@ -35,6 +35,20 @@ def filter(html, contexts = {})
     described_class.call(html, contexts)
   end
 
+  # Run text through HTML::Pipeline with the current filter and return the
+  # result Hash
+  #
+  # body     - String text to run through the pipeline
+  # contexts - Hash context for the filter. (default: {project: project})
+  #
+  # Returns the Hash of the pipeline result
+  def pipeline_result(body, contexts = {})
+    contexts.reverse_merge!(project: project)
+
+    pipeline = HTML::Pipeline.new([described_class], contexts)
+    pipeline.call(body)
+  end
+
   def allow_cross_reference!
     allow_any_instance_of(described_class).
       to receive(:user_can_reference_project?).and_return(true)