diff --git a/CHANGELOG b/CHANGELOG
index 0c3aadc29d18864144323e5747809d669a8e4dd0..72679b89d82f050f07fca846a7802197781a2096 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -13,9 +13,11 @@ v 8.9.0 (unreleased)
   - Fix endless redirections when accessing user OAuth applications when they are disabled
   - Allow enabling wiki page events from Webhook management UI
   - Bump rouge to 1.11.0
+  - Fix MR-auto-close text added to description
   - Fix issue with arrow keys not working in search autocomplete dropdown
   - Fix an issue where note polling stopped working if a window was in the
     background during a refresh.
+  - Pre-processing Markdown now only happens when needed
   - Make EmailsOnPushWorker use Sidekiq mailers queue
   - Redesign all Devise emails. !4297
   - Don't show 'Leave Project' to group members
@@ -148,6 +150,7 @@ v 8.9.0 (unreleased)
   - Ensure Todos counters doesn't count Todos for projects pending delete
   - Add left/right arrows horizontal navigation
   - Add tooltip to pin/unpin navbar
+  - Add new sub nav style to Wiki and Graphs sub navigation
 
 v 8.8.5
   - Import GitHub repositories respecting the API rate limit !4166
diff --git a/Gemfile b/Gemfile
index bc1223e1bbcaced025a0bf994bdb31dd89c04996..092ea9d69b0b300052dd11c5e07555968f710c1b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -48,7 +48,7 @@ gem 'attr_encrypted', '~> 3.0.0'
 gem 'u2f', '~> 0.2.1'
 
 # Browser detection
-gem "browser", '~> 2.0.3'
+gem "browser", '~> 2.2'
 
 # Extracting information from a git repository
 # Provide access to Gitlab::Git library
@@ -330,7 +330,7 @@ gem "newrelic_rpm", '~> 3.14'
 
 gem 'octokit', '~> 4.3.0'
 
-gem "mail_room", "~> 0.7"
+gem "mail_room", "~> 0.8"
 
 gem 'email_reply_parser', '~> 0.5.8'
 
diff --git a/Gemfile.lock b/Gemfile.lock
index 49e548fb94f0d0327e95a7cb122120476ba92ca1..ba16e4bf3378be01879b9136f96739abe5b71b58 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -98,7 +98,7 @@ GEM
       autoprefixer-rails (>= 5.2.1)
       sass (>= 3.3.4)
     brakeman (3.3.2)
-    browser (2.0.3)
+    browser (2.2.0)
     builder (3.2.2)
     bullet (5.0.0)
       activesupport (>= 3.0.0)
@@ -398,7 +398,7 @@ GEM
       systemu (~> 2.6.2)
     mail (2.6.4)
       mime-types (>= 1.16, < 4)
-    mail_room (0.7.0)
+    mail_room (0.8.0)
     method_source (0.8.2)
     mime-types (2.99.2)
     mimemagic (0.3.0)
@@ -833,7 +833,7 @@ DEPENDENCIES
   binding_of_caller (~> 0.7.2)
   bootstrap-sass (~> 3.3.0)
   brakeman (~> 3.3.0)
-  browser (~> 2.0.3)
+  browser (~> 2.2)
   bullet
   bundler-audit
   byebug
@@ -899,7 +899,7 @@ DEPENDENCIES
   license_finder
   licensee (~> 8.0.0)
   loofah (~> 2.0.3)
-  mail_room (~> 0.7)
+  mail_room (~> 0.8)
   method_source (~> 0.8)
   minitest (~> 5.7.0)
   mousetrap-rails (~> 1.4.6)
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 397892d15c03b4b7f6cb48cb36c449f50280ec29..0206db461da3f588ad2a0313d5d22129cdf3aed4 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -121,6 +121,11 @@ window.onload = ->
     setTimeout shiftWindow, 100
 
 $ ->
+
+  $document = $(document)
+  $window   = $(window)
+  $body     = $('body')
+
   gl.utils.preventDisabledButtons()
   bootstrapBreakpoint = bp.getBreakpointSize()
 
@@ -152,7 +157,7 @@ $ ->
     ), 1
 
   # Initialize tooltips
-  $('body').tooltip(
+  $body.tooltip(
     selector: '.has-tooltip, [data-toggle="tooltip"]'
     placement: (_, el) ->
       $el = $(el)
@@ -171,7 +176,7 @@ $ ->
     flash.show()
 
   # Disable form buttons while a form is submitting
-  $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
+  $body.on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
     buttons = $('[type="submit"]', @)
 
     switch e.type
@@ -184,7 +189,7 @@ $ ->
   $('.account-box').hover -> $(@).toggleClass('hover')
 
   # Commit show suppressed diff
-  $(document).on 'click', '.diff-content .js-show-suppressed-diff', ->
+  $document.on 'click', '.diff-content .js-show-suppressed-diff', ->
     $container = $(@).parent()
     $container.next('table').show()
     $container.remove()
@@ -197,13 +202,13 @@ $ ->
     $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
 
   # Show/hide comments on diff
-  $("body").on "click", ".js-toggle-diff-comments", (e) ->
+  $body.on "click", ".js-toggle-diff-comments", (e) ->
     $(@).toggleClass('active')
     $(@).closest(".diff-file").find(".notes_holder").toggle()
     e.preventDefault()
 
-  $(document).off "click", '.js-confirm-danger'
-  $(document).on "click", '.js-confirm-danger', (e) ->
+  $document.off "click", '.js-confirm-danger'
+  $document.on "click", '.js-confirm-danger', (e) ->
     e.preventDefault()
     btn = $(e.target)
     text = btn.data("confirm-danger-message")
@@ -211,7 +216,7 @@ $ ->
     new ConfirmDangerModal(form, text)
 
 
-  $(document).on 'click', 'button', ->
+  $document.on 'click', 'button', ->
     $(this).blur()
 
   $('input[type="search"]').each ->
@@ -219,7 +224,7 @@ $ ->
     $this.attr 'value', $this.val()
     return
 
-  $(document)
+  $document
     .off 'keyup', 'input[type="search"]'
     .on 'keyup', 'input[type="search"]' , (e) ->
       $this = $(this)
@@ -227,7 +232,7 @@ $ ->
 
   $sidebarGutterToggle = $('.js-sidebar-toggle')
 
-  $(document)
+  $document
     .off 'breakpoint:change'
     .on 'breakpoint:change', (e, breakpoint) ->
       if breakpoint is 'sm' or breakpoint is 'xs'
@@ -239,14 +244,14 @@ $ ->
     oldBootstrapBreakpoint = bootstrapBreakpoint
     bootstrapBreakpoint = bp.getBreakpointSize()
     if bootstrapBreakpoint != oldBootstrapBreakpoint
-      $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
+      $document.trigger('breakpoint:change', [bootstrapBreakpoint])
 
   checkInitialSidebarSize = ->
     bootstrapBreakpoint = bp.getBreakpointSize()
     if bootstrapBreakpoint is "xs" or "sm"
-      $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
+      $document.trigger('breakpoint:change', [bootstrapBreakpoint])
 
-  $(window)
+  $window
     .off "resize.app"
     .on "resize.app", (e) ->
       fitSidebarForSize()
@@ -256,14 +261,14 @@ $ ->
   new Aside()
 
   # Sidenav pinning
-  if $(window).width() < 1440 and $.cookie('pin_nav') is 'true'
+  if $window.width() < 1440 and $.cookie('pin_nav') is 'true'
     $.cookie('pin_nav', 'false', { path: '/' })
     $('.page-with-sidebar')
       .toggleClass('page-sidebar-collapsed page-sidebar-expanded')
       .removeClass('page-sidebar-pinned')
     $('.navbar-fixed-top').removeClass('header-pinned-nav')
 
-  $(document)
+  $document
     .off 'click', '.js-nav-pin'
     .on 'click', '.js-nav-pin', (e) ->
       e.preventDefault()
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee
index 584d281a510e06e2b4078938c3ade902b6f216ad..834a81af459c4c3ea5587489a6c0db989c8d201e 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee
@@ -121,7 +121,11 @@ class @ContributorsMasterGraph extends ContributorsGraph
 
 class @ContributorsAuthorGraph extends ContributorsGraph
   constructor: (@data) ->
-    @width = $('.content').width()/2 - 100
+    # Don't split graph size in half for mobile devices.
+    if $(window).width() < 768
+      @width = $('.content').width() - 80
+    else
+      @width = ($('.content').width() / 2) - 100
     @height = 200
     @x = null
     @y = null
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index d5fe5bc2ef13172af330989508e9be2990079f97..38023818709d0847059797b44c68dc338cd5fe32 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -97,6 +97,22 @@
   }
 }
 
+.sub-header-block {
+  background-color: $white-light;
+  border-bottom: 1px solid $white-dark;
+  padding: 11px 0;
+  margin-bottom: 11px;
+
+  .oneline {
+    line-height: 35px;
+  }
+
+  &.no-bottom-space {
+    border-bottom: 0;
+    margin-bottom: 0;
+  }
+}
+
 .cover-block {
   text-align: center;
   background: $background-color;
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index d4d579a083d65b17b2a3bf6b5f404f6511e1fe8a..00111dfa70662a3fec984118e1b2cc27efe14b51 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -461,10 +461,12 @@
       }
     }
 
-    .ui-state-active,
-    .ui-state-hover {
-      color: $md-link-color;
-      background-color: $calendar-hover-bg;
+    .ui-datepicker-calendar {
+      .ui-state-hover,
+      .ui-state-active {
+        color: #fff;
+        border: 0;
+      }
     }
 
     .ui-datepicker-prev,
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 694f09c04644a7b3d8de75efaba048769b001f40..0281b06d3ba3e874358c892cbd47b8c6b9d459eb 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -111,10 +111,6 @@
     width: 50%;
     line-height: 28px;
 
-    &.wiki-page {
-      padding: 16px 10px 11px;
-    }
-
     /* Small devices (phones, tablets, 768px and lower) */
     @media (max-width: $screen-sm-min) {
       width: 100%;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index a0bb3427af0e2771c24b2d8bac5db05fe635ee22..98f917ce69b5c64df22c68286801d12c520512ea 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -91,7 +91,6 @@
       text-decoration: none;
       font-weight: normal;
       outline: none;
-      white-space: nowrap;
 
       &:hover,
       &:active,
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 046c38aba44c65e51abf7fd5fe13645690dd338d..f5f67e2cd84e2401a8c35c3ef10a9a756f93b6ba 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -50,11 +50,10 @@
 
 .label-row {
   .label-name {
-    display: block;
+    display: inline-block;
     margin-bottom: 10px;
 
     @media (min-width: $screen-sm-min) {
-      display: inline-block;
       width: 200px;
       margin-bottom: 0;
     }
@@ -63,6 +62,7 @@
   .label-description {
     display: block;
     margin-bottom: 10px;
+    margin-left: 50px;
 
     @media (min-width: $screen-sm-min) {
       display: inline-block;
diff --git a/app/assets/stylesheets/pages/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss
index 85a0304196c2307a4ba5840d1c91ca7129098cf1..69288b31cc4299794d97dfe0551f062e066fdd53 100644
--- a/app/assets/stylesheets/pages/stat_graph.scss
+++ b/app/assets/stylesheets/pages/stat_graph.scss
@@ -14,24 +14,38 @@
   font-size: 10px;
 }
 
+#contributors-master {
+  @include make-md-column(12);
+
+  svg {
+    width: 100%;
+  }
+}
+
 #contributors {
   .contributors-list {
     margin: 0 0 10px;
     list-style: none;
     padding: 0;
+
+    svg {
+      width: 100%;
+    }
   }
 
   .person {
-    &:nth-child(even) {
-      float: right;
-    }
-    float: left;
+    @include make-md-column(6);
     margin-top: 10px;
+
+    @media (max-width: $screen-sm-min) {
+      width: 100%;
+    }
   }
 
   .person .spark {
     display: block;
     background: #f3f3f3;
+    width: 100%;
   }
 
   .person .area-contributor {
diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb
index 26cf74e484927636093e93bb959f2e1b2ed84943..4b0ec54b3f4f8623cc5d71752e339966dbdc7f74 100644
--- a/app/controllers/admin/appearances_controller.rb
+++ b/app/controllers/admin/appearances_controller.rb
@@ -5,6 +5,7 @@ class Admin::AppearancesController < Admin::ApplicationController
   end
 
   def preview
+    render 'preview', layout: 'devise'
   end
 
   def create
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index dd1bc6f5d52bdbf53ccbd2be32c6c3f20995878e..9cc31620d9f5313d196a9736334bd58e53c3586b 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -36,6 +36,10 @@ class ApplicationController < ActionController::Base
     render_404
   end
 
+  rescue_from Gitlab::Access::AccessDeniedError do |exception|
+    render_403
+  end
+
   def redirect_back_or_default(default: root_path, options: {})
     redirect_to request.referer.present? ? :back : default, options
   end
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index a24273fad0b232c79974ff3bc5343b1a5ebe4fb7..52dc396af6af21f8b050ce361300edd5b3e64c6d 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -21,29 +21,18 @@ module MembershipActions
 
   def leave
     @member = membershipable.members.find_by(user_id: current_user)
-    return render_403 unless @member
+    Members::DestroyService.new(@member, current_user).execute
 
     source_type = @member.real_source_type.humanize(capitalize: false)
-
-    if can?(current_user, action_member_permission(:destroy, @member), @member)
-      notice =
-        if @member.request?
-          "Your access request to the #{source_type} has been withdrawn."
-        else
-          "You left the \"#{@member.source.human_name}\" #{source_type}."
-        end
-      @member.destroy
-
-      redirect_to [:dashboard, @member.real_source_type.tableize], notice: notice
-    else
-      if cannot_leave?
-        alert = "You can not leave the \"#{@member.source.human_name}\" #{source_type}."
-        alert << " Transfer or delete the #{source_type}."
-        redirect_to polymorphic_url(membershipable), alert: alert
+    notice =
+      if @member.request?
+        "Your access request to the #{source_type} has been withdrawn."
       else
-        render_403
+        "You left the \"#{@member.source.human_name}\" #{source_type}."
       end
-    end
+    redirect_path = @member.request? ? @member.source : [:dashboard, @member.real_source_type.tableize]
+
+    redirect_to redirect_path, notice: notice
   end
 
   protected
@@ -51,8 +40,4 @@ module MembershipActions
   def membershipable
     raise NotImplementedError
   end
-
-  def cannot_leave?
-    raise NotImplementedError
-  end
 end
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index d0f2e2949f08b461ddc91f71c520d92800868cfa..2c49fe3833ecceb4d5970b720053e45fb1c0bf7e 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -36,9 +36,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
   def destroy
     @group_member = @group.group_members.find(params[:id])
 
-    return render_403 unless can?(current_user, :destroy_group_member, @group_member)
-
-    @group_member.destroy
+    Members::DestroyService.new(@group_member, current_user).execute
 
     respond_to do |format|
       format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
@@ -68,8 +66,4 @@ class Groups::GroupMembersController < Groups::ApplicationController
 
   # MembershipActions concern
   alias_method :membershipable, :group
-
-  def cannot_leave?
-    @group.last_owner?(current_user)
-  end
 end
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 35d067cd02924c7c2481f0b861b87b9ba576be97..6ba32d33403afb8fd11346ee5ba5c1ff98db3ea0 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -50,9 +50,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
   def destroy
     @project_member = @project.project_members.find(params[:id])
 
-    return render_403 unless can?(current_user, :destroy_project_member, @project_member)
-
-    @project_member.destroy
+    Members::DestroyService.new(@project_member, current_user).execute
 
     respond_to do |format|
       format.html do
@@ -98,8 +96,4 @@ class Projects::ProjectMembersController < Projects::ApplicationController
 
   # MembershipActions concern
   alias_method :membershipable, :project
-
-  def cannot_leave?
-    current_user == @project.owner
-  end
 end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index a0dafc52622eea3afa05b37a8d34d471ef768cdc..1a259656f31a9e52b7eb24584ff459e95a371734 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -50,8 +50,6 @@ module GitlabMarkdownHelper
 
     context[:project] ||= @project
 
-    text = Banzai.pre_process(text, context)
-
     html = Banzai.render(text, context)
 
     context.merge!(
diff --git a/app/models/member.rb b/app/models/member.rb
index 4ee3f1bb5c26a86b7d8dc6452381b79169b6a632..c74a16367dbd9f0f8b27e071b6096ef8fceac8bf 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -48,7 +48,6 @@ class Member < ActiveRecord::Base
   after_create :post_create_hook, unless: [:pending?, :importing?]
   after_update :post_update_hook, unless: [:pending?, :importing?]
   after_destroy :post_destroy_hook, unless: :pending?
-  after_destroy :post_decline_request, if: :request?
 
   delegate :name, :username, :email, to: :user, prefix: true
 
@@ -188,7 +187,7 @@ class Member < ActiveRecord::Base
   end
 
   def send_request
-    # override in subclass
+    notification_service.new_access_request(self)
   end
 
   def post_create_hook
@@ -215,10 +214,6 @@ class Member < ActiveRecord::Base
     post_create_hook
   end
 
-  def post_decline_request
-    # override in subclass
-  end
-
   def system_hook_service
     SystemHooksService.new
   end
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 363db8779689bf47d89aad38238232e1b8910da8..2f13d339c8970af9a6936f8c59c0563cbdcfb830 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -33,12 +33,6 @@ class GroupMember < Member
     super
   end
 
-  def send_request
-    notification_service.new_group_access_request(self)
-
-    super
-  end
-
   def post_create_hook
     notification_service.new_group_member(self)
 
@@ -64,10 +58,4 @@ class GroupMember < Member
 
     super
   end
-
-  def post_decline_request
-    notification_service.decline_group_access_request(self)
-
-    super
-  end
 end
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 250ee04fd1d1f68ac1123ff295b857d1a0b50433..e9d3a82ba15b3c6112c89816801b17a739cd7a57 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -111,12 +111,6 @@ class ProjectMember < Member
     super
   end
 
-  def send_request
-    notification_service.new_project_access_request(self)
-
-    super
-  end
-
   def post_create_hook
     unless owner?
       event_service.join_project(self.project, self.user)
@@ -152,12 +146,6 @@ class ProjectMember < Member
     super
   end
 
-  def post_decline_request
-    notification_service.decline_project_access_request(self)
-
-    super
-  end
-
   def event_service
     EventCreateService.new
   end
diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..15358f80208ea5f024b79c6b1ba78d2e465e486e
--- /dev/null
+++ b/app/services/members/destroy_service.rb
@@ -0,0 +1,21 @@
+module Members
+  class DestroyService < BaseService
+    attr_accessor :member, :current_user
+
+    def initialize(member, user)
+      @member, @current_user = member, user
+    end
+
+    def execute
+      unless member && can?(current_user, "destroy_#{member.type.underscore}".to_sym, member)
+        raise Gitlab::Access::AccessDeniedError
+      end
+
+      member.destroy
+
+      if member.request? && member.user != current_user
+        notification_service.decline_access_request(member)
+      end
+    end
+  end
+end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index 1b48899bb0a0f8fdb7465a1bad40579ca9834b8c..7fe57747265156320a76b5b4ac84c1b7cd28f19b 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -83,7 +83,7 @@ module MergeRequests
         closes_issue = "Closes ##{iid}"
 
         if merge_request.description.present?
-          merge_request.description << closes_issue.prepend("\n")
+          merge_request.description += closes_issue.prepend("\n")
         else
           merge_request.description = closes_issue
         end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 19832a19b2b3a8d2584851642ee3e53bdd0be097..590350a11e51858762c3842662d63c64c42499fe 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -181,15 +181,16 @@ class NotificationService
     end
   end
 
-  # Project access request
-  def new_project_access_request(project_member)
-    mailer.member_access_requested_email(project_member.real_source_type, project_member.id).deliver_later
+  # Members
+  def new_access_request(member)
+    mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later
   end
 
-  def decline_project_access_request(project_member)
-    mailer.member_access_denied_email(project_member.real_source_type, project_member.project.id, project_member.user.id).deliver_later
+  def decline_access_request(member)
+    mailer.member_access_denied_email(member.real_source_type, member.source_id, member.user_id).deliver_later
   end
 
+  # Project invite
   def invite_project_member(project_member, token)
     mailer.member_invited_email(project_member.real_source_type, project_member.id, token).deliver_later
   end
@@ -216,15 +217,7 @@ class NotificationService
     mailer.member_access_granted_email(project_member.real_source_type, project_member.id).deliver_later
   end
 
-  # Group access request
-  def new_group_access_request(group_member)
-    mailer.member_access_requested_email(group_member.real_source_type, group_member.id).deliver_later
-  end
-
-  def decline_group_access_request(group_member)
-    mailer.member_access_denied_email(group_member.real_source_type, group_member.group.id, group_member.user.id).deliver_later
-  end
-
+  # Group invite
   def invite_group_member(group_member, token)
     mailer.member_invited_email(group_member.real_source_type, group_member.id, token).deliver_later
   end
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index d88f3ad314d01c51bca8e6ac56a20b4296e5cff9..dc083e50178352185f73ecae02ccaec6cf60f111 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -46,7 +46,7 @@
         Maximum file size is 1MB. Pages are optimized for a 72x72 px header logo
 
   .form-actions
-    = f.submit 'Save', class: 'btn btn-save'
+    = f.submit 'Save', class: 'btn btn-save append-right-10'
     - if @appearance.persisted?
       = link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank'
 
diff --git a/app/views/admin/appearances/preview.html.haml b/app/views/admin/appearances/preview.html.haml
index dd4a64e80bc38f6ab5598a2234b74819f6ba482e..6c51639b8405f757ec18105023bd40d0b3e341f9 100644
--- a/app/views/admin/appearances/preview.html.haml
+++ b/app/views/admin/appearances/preview.html.haml
@@ -1,29 +1,9 @@
 - page_title "Preview | Appearance"
-%h3.page-title
-  Appearance settings - Preview
-%hr
+.login-box
+  .login-heading
+    %h3 Existing user? Sign in
+  %form
+    = text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
+    = password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
+    = button_tag "Sign in", class: "btn-create btn"
 
-.ui-box
-  .title
-    Sign-in page
-  %div
-    .login-page
-      .container
-        .content
-          .login-title
-            %h1= brand_title
-      %hr
-      .container
-        .content
-          .row
-            .col-sm-7
-              .brand-image
-                = brand_image
-              .brand_text
-                = brand_text
-            .col-sm-4
-              .login-box
-                %h3.page-title Sign in
-                = text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
-                = password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
-                = button_tag "Sign in", class: "btn-create btn"
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 5b8a0262ea076b8827a1dbc799ac7389cc1a0d29..50770465f0780144a42bfe175cc30d01c62b9125 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -88,28 +88,17 @@
               = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
             %hr
             = button_tag 'Add users to group', class: "btn btn-create"
+
+    = render 'shared/members/requests', membership_source: @group, members: @members.request
+
     .panel.panel-default
       .panel-heading
-        %h3.panel-title
-          Members
-          %span.badge
-            #{@group.group_members.count}
-      %ul.well-list.group-users-list
-        - @members.each do |member|
-          - user = member.user
-          %li{class: dom_class(member), id: (dom_id(user) if user)}
-            .list-item-name
-              - if user
-                %strong
-                  = link_to user.name, admin_user_path(user)
-              - else
-                %strong
-                  = member.invite_email
-                (invited)
-            %span.pull-right.light
-              = member.human_access
-              - if can?(current_user, :destroy_group_member, member)
-                = link_to group_group_member_path(@group, member), data: { confirm: remove_member_message(member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
-                  %i.fa.fa-minus.fa-inverse
+        %strong= @group.name
+        group members
+        %span.badge= @group.members.non_request.size
+        .pull-right
+          = link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@group, :members]), class: "btn btn-xs"
+      %ul.well-list.group-users-list.content-list
+        = render partial: 'shared/members/member', collection: @members.non_request, as: :member, locals: { show_controls: false }
       .panel-footer
-        = paginate @members, param_name: 'members_page', theme: 'gitlab'
+        = paginate @members.non_request, param_name: 'members_page', theme: 'gitlab'
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 9e55a562e18032d4cda87d49032792ec72fe6790..461d588415db7b8cc52d8527d0ad23eaeba63f5d 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -135,44 +135,27 @@
     - if @group
       .panel.panel-default
         .panel-heading
-          %strong #{@group.name}
-          group members (#{@group.group_members.count})
+          %strong= @group.name
+          group members
+          %span.badge= @group_members.non_request.size
           .pull-right
             = link_to admin_group_path(@group), class: 'btn btn-xs' do
-              %i.fa.fa-pencil-square-o
-        %ul.well-list
-          - @group_members.each do |member|
-            = render 'shared/members/member', member: member, show_controls: false
+              = icon('pencil-square-o', text: 'Manage Access')
+        %ul.well-list.content-list
+          = render partial: 'shared/members/member', collection: @group_members.non_request, as: :member, locals: { show_controls: false }
         .panel-footer
-          = paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
+          = paginate @group_members.non_request, param_name: 'group_members_page', theme: 'gitlab'
+
+    = render 'shared/members/requests', membership_source: @project, members: @project_members.request
 
     .panel.panel-default
       .panel-heading
-        Project members
-        %small
-          (#{@project.users.count})
+        %strong= @project.name
+        project members
+        %span.badge= @project.users.size
         .pull-right
-          = link_to namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-xs" do
-            %i.fa.fa-pencil-square-o
-            Manage Access
-      %ul.well-list.project_members
-        - @project_members.each do |project_member|
-          - user = project_member.user
-          %li.project_member
-            .list-item-name
-              - if user
-                %strong
-                  = link_to user.name, admin_user_path(user)
-              - else
-                %strong
-                  = project_member.invite_email
-                (invited)
-            .pull-right
-              - if project_member.owner?
-                %span.light Owner
-              - else
-                %span.light= project_member.human_access
-                = link_to namespace_project_project_member_path(@project.namespace, @project, project_member), data: { confirm: remove_member_message(project_member)}, method: :delete, remote: true, class: "btn btn-sm btn-remove" do
-                  %i.fa.fa-times
+          = link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@project, :members]), class: "btn btn-xs"
+      %ul.well-list.project_members.content-list
+        = render partial: 'shared/members/member', collection: @project_members.non_request, as: :member, locals: { show_controls: false }
       .panel-footer
-        = paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
+        = paginate @project_members.non_request, param_name: 'project_members_page', theme: 'gitlab'
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index a36531e095a477f070eba0f6b4769394c938e7a3..d6acade84f1ce6957b0717044c3641884f4ea582 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -17,8 +17,7 @@
     .panel-heading
       %strong #{@group.name}
       group members
-      %small
-        (#{@members.total_count})
+      %span.badge= @members.non_request.size
       .controls
         = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form'  do
           .form-group
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 8b1acd8d52ab1a2f19dde23689775b65ff5d8128..66e5ec1ad1ab86ada1cb279d09ba8db92c62be0d 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -2,7 +2,7 @@
   = render 'layouts/nav/admin_settings'
 
   %ul.nav-links.scrolling-tabs
-    .fade-left
+    %li.fade-left
       = icon('arrow-left')
     = nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do
       = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
@@ -37,5 +37,5 @@
         = link_to admin_spam_logs_path, title: "Spam Logs" do
           %span
             Spam Logs
-    .fade-right
+    %li.fade-right
       = icon('arrow-right')
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 42052373ceed161df553c59bea43409358a62fba..f7aa9fab7cfc012ca44edab25bff8b7a0ea1afe3 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -2,7 +2,7 @@
   = render 'layouts/nav/group_settings'
 
   %ul.nav-links.scrolling-tabs
-    .fade-left
+    %li.fade-left
       = icon('arrow-left')
     = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
       = link_to group_path(@group), title: 'Home' do
@@ -32,5 +32,5 @@
       = link_to group_group_members_path(@group), title: 'Members' do
         %span
           Members
-    .fade-right
+    %li.fade-right
       = icon('arrow-right')
diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml
index dac46648b9f5652741e3748c324437cb432a750b..3a24b09ab7eda3ec0e3b99186d3cf15f4255e56a 100644
--- a/app/views/layouts/nav/_group_settings.html.haml
+++ b/app/views/layouts/nav/_group_settings.html.haml
@@ -1,16 +1,22 @@
 - if current_user
-  - if access = @group.users.find_by(id: current_user.id)
-    .controls
-      .dropdown.group-settings-dropdown
-        %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
-          = icon('cog')
-          = icon('caret-down')
-        %ul.dropdown-menu.dropdown-menu-align-right
-          - if can?(current_user, :admin_group, @group)
-            = nav_link(path: 'groups#projects') do
-              = link_to projects_group_path(@group), title: 'Projects' do
-                Projects
-            %li.divider
-            %li
-              = link_to edit_group_path(@group) do
-                Edit Group
+  - can_edit = can?(current_user, :admin_group, @group)
+  - member = @group.members.non_request.find_by(user_id: current_user.id)
+  - can_leave = member && can?(current_user, :destroy_group_member, member)
+
+  .controls
+    .dropdown.group-settings-dropdown
+      %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
+        = icon('cog')
+        = icon('caret-down')
+      %ul.dropdown-menu.dropdown-menu-align-right
+        = nav_link(path: 'groups#projects') do
+          = link_to 'Projects', projects_group_path(@group), title: 'Projects'
+        %li.divider
+        - if can_edit
+          %li
+            = link_to 'Edit Group', edit_group_path(@group)
+        - if can_leave
+          %li
+            = link_to polymorphic_path([:leave, @group, :members]),
+              data: { confirm: leave_confirmation_message(@group) }, method: :delete, title: 'Leave group' do
+              Leave Group
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 7fc68398870d91ed53f0773ce3941d8354c0da08..44ea939b7e4c305726900996f719c0698c222fca 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,5 +1,5 @@
 %ul.nav-links.scrolling-tabs
-  .fade-left
+  %li.fade-left
     = icon('arrow-left')
   = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
     = link_to profile_path, title: 'Profile Settings' do
@@ -44,5 +44,5 @@
     = link_to audit_log_profile_path, title: 'Audit Log' do
       %span
         Audit Log
-  .fade-right
+  %li.fade-right
     = icon('arrow-right')
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 840777920a5bbdbc89966d346242da9d318950b7..27e840df5036417e7104b77ef8acc040cae750d6 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -5,19 +5,20 @@
         = icon('cog')
         = icon('caret-down')
       %ul.dropdown-menu.dropdown-menu-align-right
-        - is_project_member = @project.users.exists?(current_user.id)
-        - access = @project.team.max_member_access(current_user.id)
         - can_edit = can?(current_user, :admin_project, @project)
+        -# We don't use @project.team.find_member because it searches for group members too...
+        - member = @project.members.non_request.find_by(user_id: current_user.id)
+        - can_leave = member && can?(current_user, :destroy_project_member, member)
 
-        = render 'layouts/nav/project_settings', access: access, can_edit: can_edit
+        = render 'layouts/nav/project_settings', can_edit: can_edit
 
-        - if can_edit || is_project_member
+        - if can_edit || can_leave
           %li.divider
           - if can_edit
             %li
               = link_to edit_project_path(@project) do
                 Edit Project
-          - if is_project_member
+          - if can_leave
             %li
               = link_to polymorphic_path([:leave, @project, :members]),
                 data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do
@@ -25,7 +26,7 @@
 
 %div{ class: nav_control_class }
   %ul.nav-links.scrolling-tabs
-    .fade-left
+    %li.fade-left
       = icon('arrow-left')
     = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
       = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
@@ -39,9 +40,9 @@
 
     - if project_nav_tab? :files
       = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
-        = link_to project_files_path(@project), title: 'Code',  class: 'shortcuts-tree' do
+        = link_to project_files_path(@project), title: 'Repository',  class: 'shortcuts-tree' do
           %span
-            Code
+            Repository
 
     - if project_nav_tab? :pipelines
       = nav_link(controller: [:pipelines, :builds, :environments]) do
@@ -110,5 +111,5 @@
       %li.hidden
         = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
           Commits
-    .fade-right
+    %li.fade-right
       = icon('arrow-right')
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index 13d32bd1354d9bc83144141fe7f4d6244f29fceb..51a54b4f262719f5c126f0c7c3d03c45271fcd52 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -3,7 +3,7 @@
     = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
       %span
         Members
-- if access && can_edit
+- if can_edit
   - if @project.allowed_to_share_with_group?
     = nav_link(controller: :group_links) do
       = link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do
diff --git a/app/views/notify/project_was_not_exported_email.html.haml b/app/views/notify/project_was_not_exported_email.html.haml
index c9e9ade2cf1364c12462c1b1d93c21d88cd16a0f..c888da29c17de6b77745d2587e3b9bf98af446d8 100644
--- a/app/views/notify/project_was_not_exported_email.html.haml
+++ b/app/views/notify/project_was_not_exported_email.html.haml
@@ -6,4 +6,4 @@
   %ul
   - @errors.each do |error|
     %li
-      error
+      #{error}
diff --git a/app/views/notify/project_was_not_exported_email.text.erb b/app/views/notify/project_was_not_exported_email.text.erb
deleted file mode 100644
index a07f6edacf7aa8093f991375a295fbe7169dc1db..0000000000000000000000000000000000000000
--- a/app/views/notify/project_was_not_exported_email.text.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-Project <%= @project.name %> couldn't be exported.
-
-The errors we encountered were:
-
-- @errors.each do |error|
-<%= error %>
\ No newline at end of file
diff --git a/app/views/notify/project_was_not_exported_email.text.haml b/app/views/notify/project_was_not_exported_email.text.haml
new file mode 100644
index 0000000000000000000000000000000000000000..b27cb620b9e7d0f88da069943d3fcea6c677a010
--- /dev/null
+++ b/app/views/notify/project_was_not_exported_email.text.haml
@@ -0,0 +1,6 @@
+= "Project #{@project.name} couldn't be exported."
+
+= "The errors we encountered were:"
+
+- @errors.each do |error|
+  #{error}
\ No newline at end of file
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index 7236b299efc6adc84acf532a4c3a902f8c9700fc..54dab4bff07c5bc0eb8df386c1041c642a42b128 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,7 +1,7 @@
 .scrolling-tabs-container
   .nav-links.sub-nav.scrolling-tabs
     %ul{ class: (container_class) }
-      .fade-left
+      %li.fade-left
         = icon('arrow-left')
       = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
         = link_to project_files_path(@project) do
@@ -26,5 +26,5 @@
       = nav_link(controller: [:tags, :releases]) do
         = link_to namespace_project_tags_path(@project.namespace, @project) do
           Tags
-      .fade-right
+      %li.fade-right
         = icon('arrow-right')
diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml
index c322942aeba0fb4ca75b1276d16036c6c3742942..b22285c11e0df9b55c6681dde869c927460c4bff 100644
--- a/app/views/projects/compare/index.html.haml
+++ b/app/views/projects/compare/index.html.haml
@@ -3,7 +3,7 @@
 = render "projects/commits/head"
 
 %div{ class: (container_class) }
-  .row-content-block.second-block.content-component-block
+  .sub-header-block
     Compare branches, tags or commit ranges.
     %br
     Fill input field with commit id like
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index cdc34f51d6df27fe96e6317f2a91baa2340d80c8..f4ec7b767f63e1c67469f13d747454753d265502 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -1,24 +1,24 @@
+- @no_container = true
 - page_title "#{params[:from]}...#{params[:to]}"
 = render "projects/commits/head"
 
+%div{ class: (container_class) }
+  .sub-header-block.no-bottom-space
+    = render "form"
 
-.row-content-block
-  = render "form"
-
-- if @commits.present?
-  .prepend-top-default
+  - if @commits.present?
     = render "projects/commits/commit_list"
     = render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @diff_refs
-- else
-  .light-well.prepend-top-default
-    .center
-      %h4
-        There isn't anything to compare.
-      %p.slead
-        - if params[:to] == params[:from]
-          %span.label-branch #{params[:from]}
-          and
-          %span.label-branch #{params[:to]}
-          are the same.
-        - else
-          You'll need to use different branch names to get a valid comparison.
+  - else
+    .light-well
+      .center
+        %h4
+          There isn't anything to compare.
+        %p.slead
+          - if params[:to] == params[:from]
+            %span.label-branch #{params[:from]}
+            and
+            %span.label-branch #{params[:to]}
+            are the same.
+          - else
+            You'll need to use different branch names to get a valid comparison.
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index 4bcf2d9d5332ea3b6a59eb850c3a7bda9ca7c2f0..dbe9ddfde2f0877b097b715e1e43431b1dd9cf37 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -40,9 +40,3 @@
 
 
 = render 'projects', projects: @forks
-
-- if @private_forks_count > 0
-  .private-forks-notice
-    = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
-    %strong= pluralize(@private_forks_count, 'private fork')
-    %span you have no access to.
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index 8becaea246f2912f008418bc3a60afd0a8a2c29a..a388d9a0a61fc9df3b0a3dec0a83c40f31dcc018 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -1,12 +1,14 @@
-- page_specific_javascripts asset_path("graphs/application.js")
-%ul.nav-links
-  = nav_link(action: :show) do
-    = link_to 'Contributors', namespace_project_graph_path
-  = nav_link(action: :commits) do
-    = link_to 'Commits', commits_namespace_project_graph_path
-  = nav_link(action: :languages) do
-    = link_to 'Languages', languages_namespace_project_graph_path
-  - if @project.builds_enabled?
-    = nav_link(action: :ci) do
-      = link_to ci_namespace_project_graph_path do
-        Continuous Integration
+.nav-links.sub-nav
+  %ul{ class: (container_class) }
+
+    - page_specific_javascripts asset_path("graphs/application.js")
+    = nav_link(action: :show) do
+      = link_to 'Contributors', namespace_project_graph_path
+    = nav_link(action: :commits) do
+      = link_to 'Commits', commits_namespace_project_graph_path
+    = nav_link(action: :languages) do
+      = link_to 'Languages', languages_namespace_project_graph_path
+    - if @project.builds_enabled?
+      = nav_link(action: :ci) do
+        = link_to ci_namespace_project_graph_path do
+          Continuous Integration
diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml
index 19ccc125ea825f83b07583236c82880f9357b8d0..e695d3ae369ca92585e9288acbd3d3dfe376287e 100644
--- a/app/views/projects/graphs/ci.html.haml
+++ b/app/views/projects/graphs/ci.html.haml
@@ -1,15 +1,18 @@
+- @no_container = true
 - page_title "Continuous Integration", "Graphs"
 = render 'head'
-.row-content-block.append-bottom-default
-  .oneline
-    A collection of graphs for Continuous Integration
 
-#charts.ci-charts
-  .row
-    .col-md-6
-      = render 'projects/graphs/ci/overall'
-    .col-md-6
-      = render 'projects/graphs/ci/build_times'
+%div{ class: (container_class) }
+  .sub-header-block
+    .oneline
+      A collection of graphs for Continuous Integration
 
-  %hr
-  = render 'projects/graphs/ci/builds'
+  #charts.ci-charts
+    .row
+      .col-md-6
+        = render 'projects/graphs/ci/overall'
+      .col-md-6
+        = render 'projects/graphs/ci/build_times'
+
+    %hr
+    = render 'projects/graphs/ci/builds'
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index d9b2fb6c065d8f06bb64487b991205ec6557964c..0daffe68f6fd55fe7c001c983d9fbc708a809fe9 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -1,52 +1,54 @@
+- @no_container = true
 - page_title "Commits", "Graphs"
 = render 'head'
 
-.row-content-block.append-bottom-default
-  .tree-ref-holder
-    = render 'shared/ref_switcher', destination: 'graphs_commits'
-  %ul.breadcrumb.repo-breadcrumb
-    = commits_breadcrumbs
+%div{ class: (container_class) }
+  .sub-header-block
+    .tree-ref-holder
+      = render 'shared/ref_switcher', destination: 'graphs_commits'
+    %ul.breadcrumb.repo-breadcrumb
+      = commits_breadcrumbs
 
-%p.lead
-  Commit statistics for
-  %strong #{@ref}
-  #{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
+  %p.lead
+    Commit statistics for
+    %strong #{@ref}
+    #{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
 
-.row
-  .col-md-6
-    %ul
-      %li
-        %p.lead
-          %strong #{@commits_graph.commits.size}
-          commits during
-          %strong #{@commits_graph.duration}
-          days
-      %li
-        %p.lead
-          Average
-          %strong #{@commits_graph.commit_per_day}
-          commits per day
-      %li
-        %p.lead
-          Contributed by
-          %strong #{@commits_graph.authors}
-          authors
-  .col-md-6
-    %div
-      %p.slead
-        Commits per day of month
-      %canvas#month-chart
-.row
-  .col-md-6
-    %div
-      %p.slead
-        Commits per day hour (UTC)
-      %canvas#hour-chart
-  .col-md-6
-    %div
-      %p.slead
-        Commits per weekday
-      %canvas#weekday-chart
+  .row
+    .col-md-6
+      %ul
+        %li
+          %p.lead
+            %strong #{@commits_graph.commits.size}
+            commits during
+            %strong #{@commits_graph.duration}
+            days
+        %li
+          %p.lead
+            Average
+            %strong #{@commits_graph.commit_per_day}
+            commits per day
+        %li
+          %p.lead
+            Contributed by
+            %strong #{@commits_graph.authors}
+            authors
+    .col-md-6
+      %div
+        %p.slead
+          Commits per day of month
+        %canvas#month-chart
+  .row
+    .col-md-6
+      %div
+        %p.slead
+          Commits per day hour (UTC)
+        %canvas#hour-chart
+    .col-md-6
+      %div
+        %p.slead
+          Commits per weekday
+        %canvas#weekday-chart
 
 :javascript
   var responsiveChart = function (selector, data) {
diff --git a/app/views/projects/graphs/languages.html.haml b/app/views/projects/graphs/languages.html.haml
index 249c16f4709619ebc3d1c2c52200457330f6b877..6d97f552a8e83f28b64b6fd1c806405d2ece3c48 100644
--- a/app/views/projects/graphs/languages.html.haml
+++ b/app/views/projects/graphs/languages.html.haml
@@ -1,24 +1,26 @@
+- @no_container = true
 - page_title "Languages", "Graphs"
 = render 'head'
 
-.row-content-block.append-bottom-default
-  .oneline
-    Programming languages used in this repository
+%div{ class: (container_class) }
+  .sub-header-block
+    .oneline
+      Programming languages used in this repository
 
-.row
-  .col-md-8
-    %canvas#languages-chart{ height: 400 }
-  .col-md-4
-    %ul.bordered-list
-      - @languages.each do |language|
-        %li
-          %span{ style: "color: #{language[:color]}" }
-            = icon('circle')
-          &nbsp;
-          = language[:label]
-          .pull-right
-            = language[:value]
-            \%
+  .row
+    .col-md-8
+      %canvas#languages-chart{ height: 400 }
+    .col-md-4
+      %ul.bordered-list
+        - @languages.each do |language|
+          %li
+            %span{ style: "color: #{language[:color]}" }
+              = icon('circle')
+            &nbsp;
+            = language[:label]
+            .pull-right
+              = language[:value]
+              \%
 
 :javascript
   var data = #{@languages.to_json};
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index 33970e7b90912cee3848f9f6653de60441fe25ad..9f7e2a361ff7ddca93806b1f4b02d8659816a53c 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,29 +1,31 @@
+- @no_container = true
 - page_title "Contributors", "Graphs"
 = render 'head'
 
-.row-content-block.append-bottom-default
-  .tree-ref-holder
-    = render 'shared/ref_switcher', destination: 'graphs'
-  %ul.breadcrumb.repo-breadcrumb
-    = commits_breadcrumbs
-
-.loading-graph
-  .center
-    %h3.page-title
-      %i.fa.fa-spinner.fa-spin
-      Building repository graph.
-    %p.slead Please wait a moment, this page will automatically refresh when ready.
-
-.stat-graph.hide
-  .header.clearfix
-    %h3#date_header.page-title
-    %p.light
-      Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
-    %input#brush_change{:type => "hidden"}
-  .graphs
-    #contributors-master
-    #contributors.clearfix
-      %ol.contributors-list.clearfix
+%div{ class: (container_class) }
+  .sub-header-block
+    .tree-ref-holder
+      = render 'shared/ref_switcher', destination: 'graphs'
+    %ul.breadcrumb.repo-breadcrumb
+      = commits_breadcrumbs
+
+  .loading-graph
+    .center
+      %h3.page-title
+        %i.fa.fa-spinner.fa-spin
+        Building repository graph.
+      %p.slead Please wait a moment, this page will automatically refresh when ready.
+
+  .stat-graph.hide
+    .header.clearfix
+      %h3#date_header.page-title
+      %p.light
+        Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
+      %input#brush_change{:type => "hidden"}
+    .graphs.row
+      #contributors-master
+      #contributors.clearfix
+        %ol.contributors-list.clearfix
 
 
 
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index e4ab064eda8ad12242dcfca482f0b9296e39847c..593af319a4797f60209eafc429249e42a568b9fb 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -15,5 +15,5 @@
               = check_box_tag :filter_ref, 1, @options[:filter_ref]
               %span Begin with the selected commit
 
-    .network-graph{ data: { url: '#{escape_javascript(@url)}', commit_url: '#{escape_javascript(@commit_url)}', ref: '#{escape_javascript(@ref)}', commit_id: '#{escape_javascript(@commit.id)}' } }
+    .network-graph{ data: { url: @url, commit_url: @commit_url, ref: @ref, commit_id: @commit.id } }
       = spinner nil, true
diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml
index cb6136c215a9158f1eb1355e998609bc039474fe..e783d8c72c52dd82190f1b21c48844555e7910fb 100644
--- a/app/views/projects/project_members/_group_members.html.haml
+++ b/app/views/projects/project_members/_group_members.html.haml
@@ -2,8 +2,7 @@
   .panel-heading
     %strong #{@group.name}
     group members
-    %small
-      (#{members.count})
+    %span.badge= members.size
     - if can?(current_user, :admin_group_member, @group)
       .controls
         = link_to 'Manage group members',
diff --git a/app/views/projects/project_members/_shared_group_members.html.haml b/app/views/projects/project_members/_shared_group_members.html.haml
index 952844acefc3997e195d30ae05f869199a4f840a..840b57c2e6364b6af6db350808a17862f914b206 100644
--- a/app/views/projects/project_members/_shared_group_members.html.haml
+++ b/app/views/projects/project_members/_shared_group_members.html.haml
@@ -1,6 +1,7 @@
 - @project_group_links.each do |group_links|
   - shared_group = group_links.group
-  - shared_group_users_count = group_links.group.group_members.count
+  - shared_group_members = shared_group.members.non_request
+  - shared_group_users_count = shared_group_members.size
   .panel.panel-default
     .panel-heading
       Shared with
@@ -15,7 +16,7 @@
             Edit group members
     %ul.content-list
       = render partial: 'shared/members/member',
-               collection: shared_group.group_members.order(access_level: :desc).limit(20),
+               collection: shared_group_members.order(access_level: :desc).limit(20),
                as: :member,
                locals: { show_controls: false, show_roles: false }
       - if shared_group_users_count > 20
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index 03207614258b08c3f914c09cb7ebd5c9f382e168..b0bfdd235f7b24277909ee78f76705387fe8fe96 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -2,8 +2,7 @@
   .panel-heading
     %strong #{@project.name}
     project members
-    %small
-      (#{members.count})
+    %span.badge= members.size
     .controls
       = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form'  do
         .form-group
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 357ccccaf1d73b99c9f1d5b8629b3a2eccaaf434..a2026c41d014df9c90bf5f75a90819b9a35df365 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -18,7 +18,7 @@
   = render 'team', members: @project_members.non_request
 
   - if @group
-    = render "group_members", members: @group_members
+    = render "group_members", members: @group_members.non_request
 
   - if @project_group_links.any? && @project.allowed_to_share_with_group?
     = render "shared_group_members"
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 5f041aedfc0f47a099fd09fbddbb3221464a4445..15f0d85194b899ee1da71875e0a30b31603891bd 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -60,7 +60,7 @@
         - unless @repository.gitlab_ci_yml
           %li.missing
             = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
-              Set up CI
+              Set Up CI
 
 - if @repository.commit
   .content-block.second-block.white
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 4faa547769b54191430adb92f3d1f45b24066057..4ea75dbbf0cdf4d0b738b7cdf62ac8d6b64214f4 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -1,4 +1,7 @@
 - if (@page && @page.persisted?)
+  - if can?(current_user, :create_wiki, @project)
+    = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+      New Page
   = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
     Page History
   - if can?(current_user, :create_wiki, @project)
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index 988fe024e2877a2d8593d15bb8d324f5e66b3c31..f8ea479e0b113e90f0285b678037b891e7f97602 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -1,5 +1,5 @@
-.top-area
-  %ul.nav-links
+.nav-links.sub-nav
+  %ul{ class: (container_class) }
     = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
       = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
 
@@ -10,9 +10,4 @@
       = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
         Git Access
 
-  .nav-controls
-    - if can?(current_user, :create_wiki, @project)
-      = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
-        New Page
-
-= render 'projects/wikis/new'
+  = render 'projects/wikis/new'
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index 919daf0a7b2be658a4bde51c7d18a9132f91092c..4f8abcdc8e1a167b7b9908d3e011941ee4317785 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -1,14 +1,17 @@
-%div#modal-new-wiki.modal
-  .modal-dialog
-    .modal-content
-      .modal-header
-        %a.close{href: "#", "data-dismiss" => "modal"} ×
-        %h3.page-title New Wiki Page
-      .modal-body
-        %form.new-wiki-page
-          .form-group
-            = label_tag :new_wiki_path do
-              %span Page slug
-            = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true
-          .form-actions
-            = button_tag 'Create Page', class: 'build-new-wiki btn btn-create'
+- @no_container = true
+
+%div{ class: (container_class) }
+  %div#modal-new-wiki.modal
+    .modal-dialog
+      .modal-content
+        .modal-header
+          %a.close{href: "#", "data-dismiss" => "modal"} ×
+          %h3.page-title New Wiki Page
+        .modal-body
+          %form.new-wiki-page
+            .form-group
+              = label_tag :new_wiki_path do
+                %span Page slug
+              = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true
+            .form-actions
+              = button_tag 'Create Page', class: 'build-new-wiki btn btn-create'
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index cbd69ee1a73c31d7a46b09f071cac365d75a7008..bf5d09d50c20ab993b43624fe0e039fcf78c7a01 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,19 +1,24 @@
+- @no_container = true
 - page_title "Edit", @page.title.capitalize, "Wiki"
 = render 'nav'
 
-.top-area
-  .nav-text.wiki-page
-    %strong
-      - if @page.persisted?
-        = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
-      - else
-        = @page.title.capitalize
-    %span.light
-      &middot;
-      Edit Page
+%div{ class: (container_class) }
+  .top-area
+    .nav-text
+      %strong
+        - if @page.persisted?
+          = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
+        - else
+          = @page.title.capitalize
+      %span.light
+        &middot;
+        Edit Page
 
-  .nav-controls
-    = render 'main_links'
+    .nav-controls
+      - if can?(current_user, :create_wiki, @project)
+        = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+          New Page
+      = render 'main_links'
 
 
-= render 'form'
+  = render 'form'
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index ccceab6155e8aaa309ee6e16eac00f0dacf2e110..6caf7230f350d6ee4c3f5f471947bf40e1ab1105 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -1,32 +1,34 @@
+- @no_container = true
 - page_title "Git Access", "Wiki"
 
 = render 'nav'
-.row-content-block
-  %span.oneline
-    Git access for
-    %strong= @project_wiki.path_with_namespace
+%div{ class: (container_class) }
+  .sub-header-block
+    %span.oneline
+      Git access for
+      %strong= @project_wiki.path_with_namespace
 
-  .pull-right
-    = render "shared/clone_panel", project: @project_wiki
+    .pull-right
+      = render "shared/clone_panel", project: @project_wiki
 
-.git-empty.prepend-top-default
-  %fieldset
-    %legend Install Gollum:
-    %pre.dark
-      :preserve
-        gem install gollum
+  .prepend-top-default
+    %fieldset
+      %legend Install Gollum:
+      %pre.dark
+        :preserve
+          gem install gollum
 
-    %legend Clone Your Wiki:
-    %pre.dark
-      :preserve
-        git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')}
-        cd #{h @project_wiki.path}
+      %legend Clone Your Wiki:
+      %pre.dark
+        :preserve
+          git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')}
+          cd #{h @project_wiki.path}
 
-    %legend Start Gollum And Edit Locally:
-    %pre.dark
-      :preserve
-        gollum
-        == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
-        >> Thin web server (v1.5.0 codename Knife)
-        >> Maximum connections set to 1024
-        >> Listening on 0.0.0.0:4567, CTRL+C to stop
+      %legend Start Gollum And Edit Locally:
+      %pre.dark
+        :preserve
+          gollum
+          == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
+          >> Thin web server (v1.5.0 codename Knife)
+          >> Maximum connections set to 1024
+          >> Listening on 0.0.0.0:4567, CTRL+C to stop
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index 45460ed9f41ad2e1769f778dedea40baaed583b9..630ee35b70b9473104e764209db9a0f699ebee35 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -1,37 +1,37 @@
 - page_title "History", @page.title.capitalize, "Wiki"
 = render 'nav'
+%div{ class: (container_class) }
+  .top-area
+    .nav-text
+      %strong
+        = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
+      %span.light
+        &middot;
+        History
 
-.top-area
-  .nav-text
-    %strong
-      = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
-    %span.light
-      &middot;
-      History
-
-.table-holder
-  %table.table
-    %thead
-      %tr
-        %th Page version
-        %th Author
-        %th Commit Message
-        %th Last updated
-        %th Format
-    %tbody
-      - @page.versions.each_with_index do |version, index|
-        - commit = version
+  .table-holder
+    %table.table
+      %thead
         %tr
-          %td
-            = link_to project_wiki_path_with_version(@project, @page,
-                                                     commit.id, index == 0) do
-              = truncate_sha(commit.id)
-          %td
-            = commit.author.name
-          %td
-            = commit.message
-          %td
-            #{time_ago_with_tooltip(version.authored_date)}
-          %td
-            %strong
-              = @page.page.wiki.page(@page.page.name, commit.id).try(:format)
+          %th Page version
+          %th Author
+          %th Commit Message
+          %th Last updated
+          %th Format
+      %tbody
+        - @page.versions.each_with_index do |version, index|
+          - commit = version
+          %tr
+            %td
+              = link_to project_wiki_path_with_version(@project, @page,
+                                                       commit.id, index == 0) do
+                = truncate_sha(commit.id)
+            %td
+              = commit.author.name
+            %td
+              = commit.message
+            %td
+              #{time_ago_with_tooltip(version.authored_date)}
+            %td
+              %strong
+                = @page.page.wiki.page(@page.page.name, commit.id).try(:format)
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index 2f6162fa3c5f01d7ccb384e882d1e8c5ef072638..81d9f391c1c50f435282afe30a2a3dec4f474416 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -1,12 +1,14 @@
+- @no_container = true
 - page_title "Pages", "Wiki"
 
 = render 'nav'
 
-%ul.content-list
-  - @wiki_pages.each do |wiki_page|
-    %li
-      = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
-      %small (#{wiki_page.format})
-      .pull-right
-        %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
-= paginate @wiki_pages, theme: 'gitlab'
+%div{ class: (container_class) }
+  %ul.content-list
+    - @wiki_pages.each do |wiki_page|
+      %li
+        = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
+        %small (#{wiki_page.format})
+        .pull-right
+          %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
+  = paginate @wiki_pages, theme: 'gitlab'
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index 9166c0edb3b6e5af7e399803e975116f237a414d..76f9b1ecd762b7ddda0e9a6e4735331491754af9 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -1,24 +1,26 @@
+- @no_container = true
 - page_title @page.title.capitalize, "Wiki"
 = render 'nav'
 
-.top-area
-  .nav-text
-    %strong= @page.title.capitalize
+%div{ class: (container_class) }
+  .top-area
+    .nav-text
+      %strong= @page.title.capitalize
 
-    %span.wiki-last-edit-by
-      &middot;
-      last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
+      %span.wiki-last-edit-by
+        &middot;
+        last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
 
-  .nav-controls
-    = render 'main_links'
+    .nav-controls
+      = render 'main_links'
 
-- if @page.historical?
-  .warning_message
-    This is an old version of this page.
-    You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
+  - if @page.historical?
+    .warning_message
+      This is an old version of this page.
+      You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
 
 
-.wiki-holder.prepend-top-default.append-bottom-default
-  .wiki
-    = preserve do
-      = render_wiki_content(@page)
+  .wiki-holder.prepend-top-default.append-bottom-default
+    .wiki
+      = preserve do
+        = render_wiki_content(@page)
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index f96cc981c8d61feff2980619bd27775d7cc25a79..aa18e6f236f0e9580564f6121c43e61eaa0d1cdb 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,9 +1,9 @@
 %ul.nav-links.event-filter.scrolling-tabs
-  .fade-left
+  %li.fade-left
     = icon('arrow-left')
   = event_filter_link EventFilter.push, 'Push events'
   = event_filter_link EventFilter.merged, 'Merge events'
   = event_filter_link EventFilter.comments, 'Comments'
   = event_filter_link EventFilter.team, 'Team'
-  .fade-right
+  %li.fade-right
     = icon('arrow-right')
diff --git a/app/views/shared/members/_requests.html.haml b/app/views/shared/members/_requests.html.haml
index b5963876034533e5ca288be635653559805e830b..e4bd2bdc265d4f40365293ac6a03bce8223feaf6 100644
--- a/app/views/shared/members/_requests.html.haml
+++ b/app/views/shared/members/_requests.html.haml
@@ -3,6 +3,6 @@
     .panel-heading
       %strong= membership_source.name
       access requests
-      %small= "(#{members.size})"
+      %span.badge= members.size
     %ul.content-list
       = render partial: 'shared/members/member', collection: members, as: :member
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 2e08bb2ac08444fa370176b199c2a76941c0afda..3a9dd37dc7d8a53c4b875d7db7658c71f7a48523 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -16,6 +16,12 @@
         = render "shared/projects/project", project: project, skip_namespace: skip_namespace,
           avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar,
           forks: forks, show_last_commit_as_description: show_last_commit_as_description
+
+      - if @private_forks_count && @private_forks_count > 0
+        %li.project-row.private-forks-notice
+          = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
+          %strong= pluralize(@private_forks_count, 'private fork')
+          %span you have no access to.
     = paginate(projects, remote: remote, theme: "gitlab") if projects.respond_to? :total_pages
   - else
     .nothing-here-block No projects found
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 09ffc319065df5dad9c26eb764ac82f3b83ab9c4..c6dc1e4ab38da2b2a162c878665d82ef7bbbc1e9 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -215,7 +215,7 @@ Settings.gitlab.default_projects_features['container_registry'] = true if Settin
 Settings.gitlab.default_projects_features['visibility_level']   = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
 Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
 Settings.gitlab['restricted_signup_domains'] ||= []
-Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git']
+Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project]
 Settings.gitlab['trusted_proxies'] ||= []
 
 
diff --git a/doc/README.md b/doc/README.md
index 495a99d5d9d2396c125b871d67c59d485daf48ce..f1283cea0ad16c114dd5b6065be6a9b850dd9221 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -3,18 +3,18 @@
 ## User documentation
 
 - [API](api/README.md) Automate GitLab via a simple and powerful API.
-- [CI](ci/README.md) GitLab Continuous Integration (CI) getting started, `.gitlab-ci.yml` options, and examples.
+- [CI/CD](ci/README.md) GitLab Continuous Integration (CI) and Continuous Delivery (CD) getting started, `.gitlab-ci.yml` options, and examples.
 - [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
+- [Container Registry](container_registry/README.md) Learn how to use GitLab Container Registry.
 - [GitLab Basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab.
 - [Importing to GitLab](workflow/importing/README.md).
 - [Importing and exporting projects between instances](user/project/settings/import_export.md).
 - [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
-- [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab
+- [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab.
 - [Permissions](permissions/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
 - [Profile Settings](profile/README.md)
 - [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
 - [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
-- [Container Registry](container_registry/README.md) Learn how to use GitLab Container Registry.
 - [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
 - [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
 - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
@@ -25,15 +25,15 @@
   external authentication with LDAP, SAML, CAS and additional Omniauth providers.
 - [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when webhooks aren't enough.
 - [Install](install/README.md) Requirements, directory structures and installation from source.
-- [Restart GitLab](administration/restart_gitlab.md) Learn how to restart GitLab and its components
+- [Restart GitLab](administration/restart_gitlab.md) Learn how to restart GitLab and its components.
 - [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, Twitter.
 - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
 - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
 - [Log system](administration/logs.md) Log system.
 - [Environment Variables](administration/environment_variables.md) to configure GitLab.
-- [Operations](operations/README.md) Keeping GitLab up and running
+- [Operations](operations/README.md) Keeping GitLab up and running.
 - [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects.
-- [Repository checks](administration/repository_checks.md) Periodic Git repository checks
+- [Repository checks](administration/repository_checks.md) Periodic Git repository checks.
 - [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
 - [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
 - [Update](update/README.md) Update guides to upgrade your installation.
@@ -42,11 +42,11 @@
 - [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
 - [Git LFS configuration](workflow/lfs/lfs_administration.md)
 - [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
-- [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics
-- [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint
-- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs
-- [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability
-- [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab
+- [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
+- [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint.
+- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs.
+- [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability.
+- [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab.
 
 ## Contributor documentation
 
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index 7870669fa7767973958132db165a6b2d3c781101..de225385c7addd82fc74803bd5fa6e2e9edda0de 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -22,6 +22,7 @@ You can read more about Docker Registry at https://docs.docker.com/registry/intr
 - [Disable Container Registry per project](#disable-container-registry-per-project)
 - [Disable Container Registry for new projects site-wide](#disable-container-registry-for-new-projects-site-wide)
 - [Container Registry storage path](#container-registry-storage-path)
+- [Container Registry storage driver](#container-registry-storage-driver)
 - [Storage limitations](#storage-limitations)
 - [Changelog](#changelog)
 
@@ -306,8 +307,12 @@ the Container Registry by themselves, follow the steps below.
 
 ## Container Registry storage path
 
-To change the storage path where Docker images will be stored, follow the
-steps below.
+>**Note:**
+For configuring storage in the cloud instead of the filesystem, see the
+[storage driver configuration](#container-registry-storage-driver).
+
+If you want to store your images on the filesystem, you can change the storage
+path for the Container Registry, follow the steps below.
 
 This path is accessible to:
 
@@ -349,6 +354,72 @@ The default location where images are stored in source installations, is
 
 1. Save the file and [restart GitLab][] for the changes to take effect.
 
+## Container Registry storage driver
+
+You can configure the Container Registry to use a different storage backend by
+configuring a different storage driver. By default the GitLab Container Registry
+is configured to use the filesystem driver, which makes use of [storage path](#container-registry-storage-path)
+configuration.
+
+The different supported drivers are:
+
+| Driver     | Description                         |
+|------------|-------------------------------------|
+| filesystem | Uses a path on the local filesystem |
+| azure      | Microsoft Azure Blob Storage        |
+| gcs        | Google Cloud Storage                |
+| s3         | Amazon Simple Storage Service       |
+| swift      | OpenStack Swift Object Storage      |
+| oss        | Aliyun OSS                          |
+
+Read more about the individual driver's config options in the
+[Docker Registry docs][storage-config].
+
+> **Warning** GitLab will not backup Docker images that are not stored on the
+filesystem. Remember to enable backups with your object storage provider if
+desired.
+
+---
+
+**Omnibus GitLab installations**
+
+To configure the storage driver in Omnibus:
+
+1. Edit `/etc/gitlab/gitlab.rb`:
+
+    ```ruby
+    registry['storage'] = {
+      's3' => {
+        'accesskey' => 's3-access-key',
+        'secretkey' => 's3-secret-key-for-access-key',
+        'bucket' => 'your-s3-bucket'
+      }
+    }
+    ```
+
+1. Save the file and [reconfigure GitLab][] for the changes to take effect.
+
+---
+
+**Installations from source**
+
+Configuring the storage driver is done in your registry config YML file created
+when you [deployed your docker registry][registry-deploy].
+
+Example:
+
+```
+storage:
+  s3:
+    accesskey: 'AKIAKIAKI'
+    secretkey: 'secret123'
+    bucket: 'gitlab-registry-bucket-AKIAKIAKI'
+  cache:
+    blobdescriptor: inmemory
+  delete:
+    enabled: true
+```
+
 ## Storage limitations
 
 Currently, there is no storage limitation, which means a user can upload an
diff --git a/doc/api/README.md b/doc/api/README.md
index 73f4460368854abdd835c99058eae86a243ec9c2..288f7f9ee6998ac75a0a4adc5cee086068a80f31 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -32,6 +32,7 @@ following locations:
 - [Services](services.md)
 - [Session](session.md)
 - [Settings](settings.md)
+- [Sidekiq metrics](sidekiq_metrics.md)
 - [System Hooks](system_hooks.md)
 - [Tags](tags.md)
 - [Users](users.md)
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 5a1cb5319c60124fc8e4107c3b0b548fcea55a1e..3dd4e2bc2309e3028953c576805a433725343233 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -5,6 +5,7 @@
 - [Get started with GitLab CI](quick_start/README.md)
 - [CI examples for various languages](examples/README.md)
 - [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md)
+- [Pipelines and builds](pipelines.md)
 - [Environments and deployments](environments.md)
 - [Learn how `.gitlab-ci.yml` works](yaml/README.md)
 - [Configure a Runner, the application that runs your builds](runners/README.md)
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index 040379bb38151232d4ae26897ef2605ce93c0ca5..d85b8a34cedb21f90f027ce25c1d2626ba67a51f 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -52,7 +52,7 @@ Clicking on an environment will show the history of deployments.
 Only deploys that happen after your `.gitlab-ci.yml` is properly configured will
 show up in the environments and deployments lists.
 
-[Pipelines]: quick_start/README.md
+[Pipelines]: pipelines.md
 [jobs]: yaml/README.md#jobs
 [environments]: #environments
 [deployments]: #deployments
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
new file mode 100644
index 0000000000000000000000000000000000000000..48a9f99475954b5774d135ace728adbe26489d14
--- /dev/null
+++ b/doc/ci/pipelines.md
@@ -0,0 +1,38 @@
+# Introduction to pipelines and builds
+
+>**Note:**
+Introduced in GitLab 8.8.
+
+## Pipelines
+
+A pipeline is a group of [builds] that get executed in [stages] (batches). All
+of the builds in a stage are executed in parallel (if there are enough
+concurrent [runners]), and if they all succeed, the pipeline moves on to the
+next stage. If one of the builds fails, the next stage is not (usually)
+executed.
+
+## Builds
+
+Builds are individual runs of [jobs]. Not to be confused with a `build` job or
+`build` stage.
+
+## Defining pipelines
+
+Pipelines are defined in `.gitlab-ci.yml` by specifying [jobs] that run in
+[stages].
+
+See full [documentation](yaml/README.md#jobs).
+
+## Seeing pipeline status
+
+You can find the current and historical pipeline runs under **Pipelines** for your
+project.
+
+## Seeing build status
+
+Clicking on a pipeline will show the builds that were run for that pipeline.
+
+[builds]: #builds
+[jobs]: yaml/README.md#jobs
+[stages]: yaml/README.md#stages
+[runners]: runners/README.md
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index 386b8e29fcfb7a85653b029a7a9cf9ee69f06f14..7fa1a478f344d8d12a1108f03639e9f3a0ac9b7e 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -4,41 +4,41 @@
 is fully integrated into GitLab itself and is [enabled] by default on all
 projects.
 
-The TL;DR version of how GitLab CI works is the following.
-
----
-
 GitLab offers a [continuous integration][ci] service. If you
 [add a `.gitlab-ci.yml` file][yaml] to the root directory of your repository,
 and configure your GitLab project to use a [Runner], then each merge request or
-push triggers a build.
+push triggers your CI [pipeline].
 
-The `.gitlab-ci.yml` file tells the GitLab runner what to do. By default it
-runs three [stages]: `build`, `test`, and `deploy`.
+The `.gitlab-ci.yml` file tells the GitLab runner what to do. By default it runs
+a pipeline with three [stages]: `build`, `test`, and `deploy`. You don't need to
+use all three stages; stages with no jobs are simply ignored.
 
 If everything runs OK (no non-zero return values), you'll get a nice green
 checkmark associated with the pushed commit or merge request. This makes it
-easy to see whether a merge request will cause any of the tests to fail before
+easy to see whether a merge request caused any of the tests to fail before
 you even look at the code.
 
-Most projects only use GitLab's CI service to run the test suite so that
+Most projects use GitLab's CI service to run the test suite so that
 developers get immediate feedback if they broke something.
 
+There's a growing trend to use continuous delivery and continuous deployment to
+automatically deploy tested code to staging and production environments.
+
 So in brief, the steps needed to have a working CI can be summed up to:
 
 1. Add `.gitlab-ci.yml` to the root directory of your repository
 1. Configure a Runner
 
-From there on, on every push to your Git repository, the build will be
-automagically started by the Runner and will appear under the project's
-`/builds` page.
+From there on, on every push to your Git repository, the Runner will
+automagically start the pipeline and the pipeline will appear under the
+project's `/pipelines` page.
 
 ---
 
 This guide assumes that you:
 
 - have a working GitLab instance of version 8.0 or higher or are using
-  [GitLab.com](https://gitlab.com/users/sign_in)
+  [GitLab.com](https://gitlab.com)
 - have a project in GitLab that you would like to use CI for
 
 Let's break it down to pieces and work on solving the GitLab CI puzzle.
@@ -57,15 +57,14 @@ On any push to your repository, GitLab will look for the `.gitlab-ci.yml`
 file and start builds on _Runners_ according to the contents of the file,
 for that commit.
 
-Because `.gitlab-ci.yml` is in the repository, it is version controlled,
-old versions still build successfully, forks can easily make use of CI,
-branches can have separate builds and you have a single source of truth for CI.
-You can read more about the reasons why we are using `.gitlab-ci.yml`
-[in our blog about it][blog-ci].
+Because `.gitlab-ci.yml` is in the repository and is version controlled, old
+versions still build successfully, forks can easily make use of CI, branches can
+have different pipelines and jobs, and you have a single source of truth for CI.
+You can read more about the reasons why we are using `.gitlab-ci.yml` [in our
+blog about it][blog-ci].
 
 **Note:** `.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file
-so you have to pay extra attention to the indentation. Always use spaces, not
-tabs.
+so you have to pay extra attention to indentation. Always use spaces, not tabs.
 
 ### Creating a simple `.gitlab-ci.yml` file
 
@@ -108,7 +107,7 @@ If you want to check whether your `.gitlab-ci.yml` file is valid, there is a
 Lint tool under the page `/ci/lint` of your GitLab instance. You can also find
 the link under **Settings > CI settings** in your project.
 
-For more information and a complete `.gitlab-ci.yml` syntax, please check
+For more information and a complete `.gitlab-ci.yml` syntax, please read
 [the documentation on .gitlab-ci.yml](../yaml/README.md).
 
 ### Push `.gitlab-ci.yml` to GitLab
@@ -122,7 +121,8 @@ git commit -m "Add .gitlab-ci.yml"
 git push origin master
 ```
 
-Now if you go to the **Builds** page you will see that the builds are pending.
+Now if you go to the **Pipelines** page you will see that the pipeline is
+pending.
 
 You can also go to the **Commits** page and notice the little clock icon next
 to the commit SHA.
@@ -138,15 +138,14 @@ Notice that there are two jobs pending which are named after what we wrote in
 `.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured
 yet for these builds.
 
-The next step is to configure a Runner so that it picks the pending jobs.
+The next step is to configure a Runner so that it picks the pending builds.
 
 ## Configuring a Runner
 
-In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`.
-A Runner can be a virtual machine, a VPS, a bare-metal machine, a docker
-container or even a cluster of containers. GitLab and the Runners communicate
-through an API, so the only needed requirement is that the machine on which the
-Runner is configured to have Internet access.
+In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. A Runner
+can be a virtual machine, a VPS, a bare-metal machine, a docker container or
+even a cluster of containers. GitLab and the Runners communicate through an API,
+so the only requirement is that the Runner's machine has Internet access.
 
 A Runner can be specific to a certain project or serve multiple projects in
 GitLab. If it serves all projects it's called a _Shared Runner_.
@@ -188,12 +187,16 @@ To enable **Shared Runners** you have to go to your project's
 
 [Read more on Shared Runners](../runners/README.md).
 
-## Seeing the status of your build
+## Seeing the status of your pipeline and builds
 
 After configuring the Runner successfully, you should see the status of your
 last commit change from _pending_ to either _running_, _success_ or _failed_.
 
-You can view all builds, by going to the **Builds** page in your project.
+You can view all pipelines by going to the **Pipelines** page in your project.
+
+![Commit status](img/pipelines_status.png)
+
+Or you can view all builds, by going to the **Pipelines > Builds** page.
 
 ![Commit status](img/builds_status.png)
 
@@ -238,3 +241,4 @@ CI with various languages.
 [runner]: ../runners/README.md
 [enabled]: ../enable_or_disable_ci.md
 [stages]: ../yaml/README.md#stages
+[pipeline]: ../pipelines.md
diff --git a/doc/ci/quick_start/img/pipelines_status.png b/doc/ci/quick_start/img/pipelines_status.png
new file mode 100644
index 0000000000000000000000000000000000000000..6bc97bb739cedf27bdebe43a14a1606ff380a2f6
Binary files /dev/null and b/doc/ci/quick_start/img/pipelines_status.png differ
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index d0fbcbe9988f06b742f1d62d29cd9f6bd76f8f54..b134b5cd5d3daa3fee503ad2e9ce348dcb640092 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -54,7 +54,7 @@ of your repository and contains definitions of how your project should be built.
 
 The YAML file defines a set of jobs with constraints stating when they should
 be run. The jobs are defined as top-level elements with a name and always have
-to contain the `script` clause:
+to contain at least the `script` clause:
 
 ```yaml
 job1:
@@ -165,7 +165,7 @@ stages:
 
 There are also two edge cases worth mentioning:
 
-1. If no `stages` is defined in `.gitlab-ci.yml`, then by default the `build`,
+1. If no `stages` are defined in `.gitlab-ci.yml`, then by default the `build`,
    `test` and `deploy` are allowed to be used as job's stage by default.
 2. If a job doesn't specify a `stage`, the job is assigned the `test` stage.
 
diff --git a/features/admin/groups.feature b/features/admin/groups.feature
index ab7de7ac31547fb02d60df5a267da742f3122a1d..657e847cf4ae3c4f3a9285614ca222cf5fe50f64 100644
--- a/features/admin/groups.feature
+++ b/features/admin/groups.feature
@@ -26,13 +26,6 @@ Feature: Admin Groups
     When I visit group page
     Then I should see project shared with group
 
-  @javascript
-  Scenario: Remove user from group
-    Given we have user "John Doe" in group
-    When I visit admin group page
-    And I remove user "John Doe" from group
-    Then I should not see "John Doe" in team list
-
   @javascript
   Scenario: Invite user to a group by e-mail
     When I visit admin group page
diff --git a/features/dashboard/group.feature b/features/dashboard/group.feature
index e3c01db2ebb113dc73e509f501f54896ec99639e..3ae2c679dc1a8aa7dd7842854c89c239ccce797d 100644
--- a/features/dashboard/group.feature
+++ b/features/dashboard/group.feature
@@ -5,53 +5,9 @@ Feature: Dashboard Group
     And "John Doe" is owner of group "Owned"
     And "John Doe" is guest of group "Guest"
 
-  # Leave groups
-
-  @javascript
-  Scenario: Owner should be able to leave from group if he is not the last owner
-    Given "Mary Jane" is owner of group "Owned"
-    When I visit dashboard groups page
-    Then I should see group "Owned" in group list
-    Then I should see group "Guest" in group list
-    When I click on the "Leave" button for group "Owned"
-    And I visit dashboard groups page
-    Then I should not see group "Owned" in group list
-    Then I should see group "Guest" in group list
-
-  @javascript
-  Scenario: Owner should not be able to leave from group if he is the last owner
-    Given "Mary Jane" is guest of group "Owned"
-    When I visit dashboard groups page
-    Then I should see group "Owned" in group list
-    Then I should see group "Guest" in group list
-    When I click on the "Leave" button for group "Owned"
-    Then I should see the "Can not leave message"
-
-  @javascript
-  Scenario: Guest should be able to leave from group
-    Given "Mary Jane" is guest of group "Guest"
-    When I visit dashboard groups page
-    Then I should see group "Owned" in group list
-    Then I should see group "Guest" in group list
-    When I click on the "Leave" button for group "Guest"
-    When I visit dashboard groups page
-    Then I should see group "Owned" in group list
-    Then I should not see group "Guest" in group list
-
-  @javascript
-  Scenario: Guest should be able to leave from group even if he is the only user in the group
-    When I visit dashboard groups page
-    Then I should see group "Owned" in group list
-    Then I should see group "Guest" in group list
-    When I click on the "Leave" button for group "Guest"
-    When I visit dashboard groups page
-    Then I should see group "Owned" in group list
-    Then I should not see group "Guest" in group list
-
   Scenario: Create a group from dasboard
     And I visit dashboard groups page
     And I click new group link
     And submit form with new group "Samurai" info
     Then I should be redirected to group "Samurai" page
     And I should see newly created group "Samurai"
-
diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature
index c4f987a7923fd218b3f52f6001fc9e487ee67dce..57dda9c2234fbe0471c5a4ba3b1bbf7af12354eb 100644
--- a/features/project/active_tab.feature
+++ b/features/project/active_tab.feature
@@ -10,9 +10,9 @@ Feature: Project Active Tab
     Then the active main tab should be Home
     And no other main tabs should be active
 
-  Scenario: On Project Code
+  Scenario: On Project Repository
     Given I visit my project's files page
-    Then the active main tab should be Code
+    Then the active main tab should be Repository
     And no other main tabs should be active
 
   Scenario: On Project Issues
@@ -59,46 +59,46 @@ Feature: Project Active Tab
     And no other sub navs should be active
     And the active main tab should be Settings
 
-  # Sub Tabs: Code
+  # Sub Tabs: Repository
 
-  Scenario: On Project Code/Files
+  Scenario: On Project Repository/Files
     Given I visit my project's files page
     Then the active sub tab should be Files
     And no other sub tabs should be active
-    And the active main tab should be Code
+    And the active main tab should be Repository
 
-  Scenario: On Project Code/Commits
+  Scenario: On Project Repository/Commits
     Given I visit my project's commits page
     Then the active sub tab should be Commits
     And no other sub tabs should be active
-    And the active main tab should be Code
+    And the active main tab should be Repository
 
-  Scenario: On Project Code/Network
+  Scenario: On Project Repository/Network
     Given I visit my project's network page
     Then the active sub tab should be Network
     And no other sub tabs should be active
-    And the active main tab should be Code
+    And the active main tab should be Repository
 
-  Scenario: On Project Code/Compare
+  Scenario: On Project Repository/Compare
     Given I visit my project's commits page
     And I click the "Compare" tab
     Then the active sub tab should be Compare
     And no other sub tabs should be active
-    And the active main tab should be Code
+    And the active main tab should be Repository
 
-  Scenario: On Project Code/Branches
+  Scenario: On Project Repository/Branches
     Given I visit my project's commits page
     And I click the "Branches" tab
     Then the active sub tab should be Branches
     And no other sub tabs should be active
-    And the active main tab should be Code
+    And the active main tab should be Repository
 
-  Scenario: On Project Code/Tags
+  Scenario: On Project Repository/Tags
     Given I visit my project's commits page
     And I click the "Tags" tab
     Then the active sub tab should be Tags
     And no other sub tabs should be active
-    And the active main tab should be Code
+    And the active main tab should be Repository
 
   Scenario: On Project Issues/Browse
     Given I visit my project's issues page
diff --git a/features/project/shortcuts.feature b/features/project/shortcuts.feature
index c73d0b323376899f2b26255e23a51439c90f965f..f71f69ef060d468d0c8d0fb4afa7a2aab1526e79 100644
--- a/features/project/shortcuts.feature
+++ b/features/project/shortcuts.feature
@@ -8,21 +8,21 @@ Feature: Project Shortcuts
   @javascript
   Scenario: Navigate to files tab
     Given I press "g" and "f"
-    Then the active main tab should be Code
+    Then the active main tab should be Repository
     Then the active sub tab should be Files
 
   @javascript
   Scenario: Navigate to commits tab
     Given I visit my project's files page
     Given I press "g" and "c"
-    Then the active main tab should be Code
+    Then the active main tab should be Repository
     Then the active sub tab should be Commits
 
   @javascript
   Scenario: Navigate to network tab
     Given I press "g" and "n"
     Then the active sub tab should be Network
-    And the active main tab should be Code
+    And the active main tab should be Repository
 
   @javascript
   Scenario: Navigate to graphs tab
diff --git a/features/steps/admin/groups.rb b/features/steps/admin/groups.rb
index e1f1db2872fdad292b8e8ca628a2b094318be028..8613dc537cc71a2a5925f1cc2763533451db728e 100644
--- a/features/steps/admin/groups.rb
+++ b/features/steps/admin/groups.rb
@@ -62,7 +62,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
 
   step 'I should see "johndoe@gitlab.com" in team list in every project as "Reporter"' do
     page.within ".group-users-list" do
-      expect(page).to have_content "johndoe@gitlab.com (invited)"
+      expect(page).to have_content "johndoe@gitlab.com – Invited by"
       expect(page).to have_content "Reporter"
     end
   end
@@ -92,12 +92,6 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
     current_group.add_reporter(user_john)
   end
 
-  step 'I remove user "John Doe" from group' do
-    page.within "#user_#{user_john.id}" do
-      click_link 'Remove user from group'
-    end
-  end
-
   step 'I should not see "John Doe" in team list' do
     page.within ".group-users-list" do
       expect(page).not_to have_content "John Doe"
diff --git a/features/steps/dashboard/group.rb b/features/steps/dashboard/group.rb
index 9b79a3be49b3a2f373265c128c8a351e938f5e22..cf679fea5308f7c9104f82e10850e59895870cf8 100644
--- a/features/steps/dashboard/group.rb
+++ b/features/steps/dashboard/group.rb
@@ -4,44 +4,6 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
   include SharedPaths
   include SharedUser
 
-  # Leave
-
-  step 'I click on the "Leave" button for group "Owned"' do
-    find(:css, 'li', text: "Owner").find(:css, 'i.fa.fa-sign-out').click
-    # poltergeist always confirms popups.
-  end
-
-  step 'I click on the "Leave" button for group "Guest"' do
-    find(:css, 'li', text: "Guest").find(:css, 'i.fa.fa-sign-out').click
-    # poltergeist always confirms popups.
-  end
-
-  step 'I should not see the "Leave" button for group "Owned"' do
-    expect(find(:css, 'li', text: "Owner")).not_to have_selector(:css, 'i.fa.fa-sign-out')
-    # poltergeist always confirms popups.
-  end
-
-  step 'I should not see the "Leave" button for groupr "Guest"' do
-    expect(find(:css, 'li', text: "Guest")).not_to have_selector(:css,  'i.fa.fa-sign-out')
-    # poltergeist always confirms popups.
-  end
-
-  step 'I should see group "Owned" in group list' do
-    expect(page).to have_content("Owned")
-  end
-
-  step 'I should not see group "Owned" in group list' do
-    expect(page).not_to have_content("Owned")
-  end
-
-  step 'I should see group "Guest" in group list' do
-    expect(page).to have_content("Guest")
-  end
-
-  step 'I should not see group "Guest" in group list' do
-    expect(page).not_to have_content("Guest")
-  end
-
   step 'I click new group link' do
     click_link "New Group"
   end
@@ -60,8 +22,4 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
     expect(page).to have_content "Samurai"
     expect(page).to have_content "Tokugawa Shogunate"
   end
-
-  step 'I should see the "Can not leave message"' do
-    expect(page).to have_content "You can not leave the \"Owned\" group."
-  end
 end
diff --git a/features/steps/project/project_find_file.rb b/features/steps/project/project_find_file.rb
index 47de4b91df1ebaa0b53f5f4af7e22e8c8bd6b8a6..90771847909e1c3bb3677a1f3c4cc0285fb72f34 100644
--- a/features/steps/project/project_find_file.rb
+++ b/features/steps/project/project_find_file.rb
@@ -13,12 +13,12 @@ class Spinach::Features::ProjectFindFile < Spinach::FeatureSteps
   end
 
   step 'I should see "find file" page' do
-    ensure_active_main_tab('Code')
+    ensure_active_main_tab('Repository')
     expect(page).to have_selector('.file-finder-holder', count: 1)
   end
 
   step 'I fill in Find by path with "git"' do
-    ensure_active_main_tab('Code')
+    ensure_active_main_tab('Repository')
     expect(page).to have_selector('.file-finder-holder', count: 1)
   end
 
diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb
index bfee87933010d210f40d2b8f186f18da2c78ab3c..d6024212601f060340dd267f697e28adec52d275 100644
--- a/features/steps/shared/project_tab.rb
+++ b/features/steps/shared/project_tab.rb
@@ -8,8 +8,8 @@ module SharedProjectTab
     ensure_active_main_tab('Project')
   end
 
-  step 'the active main tab should be Code' do
-    ensure_active_main_tab('Code')
+  step 'the active main tab should be Repository' do
+    ensure_active_main_tab('Repository')
   end
 
   step 'the active main tab should be Graphs' do
diff --git a/lib/banzai.rb b/lib/banzai.rb
index b467413a7dd8127c30c452af7a7af1d8917b5441..093382261ae8552645b1c3cd2cb44ce8c0bb3538 100644
--- a/lib/banzai.rb
+++ b/lib/banzai.rb
@@ -7,10 +7,6 @@ module Banzai
     Renderer.render_result(text, context)
   end
 
-  def self.pre_process(text, context)
-    Renderer.pre_process(text, context)
-  end
-
   def self.post_process(html, context)
     Renderer.post_process(html, context)
   end
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index c14a9c4c72218fea9baef0482a0dbc88f20c784c..6718acdef7e017fc304748fb771b0bf05424e7d4 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -30,13 +30,9 @@ module Banzai
     end
 
     def self.render_result(text, context = {})
-      Pipeline[context[:pipeline]].call(text, context)
-    end
+      text = Pipeline[:pre_process].to_html(text, context) if text
 
-    def self.pre_process(text, context)
-      pipeline = Pipeline[:pre_process]
-
-      pipeline.to_html(text, context)
+      Pipeline[context[:pipeline]].call(text, context)
     end
 
     # Perform post-processing on an HTML String
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index 6d0e30e916f9fe2993d56f4bd45e18a443147828..831f1e635baca56b041c8c4e1f0c0de61eb3fb47 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -5,6 +5,8 @@
 #
 module Gitlab
   module Access
+    class AccessDeniedError < StandardError; end
+
     GUEST     = 10
     REPORTER  = 20
     DEVELOPER = 30
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index 89c2c26a367630f87304f59d4d42a9290be5ec89..c8601341d54457927894554320a8b3eb672f6a3e 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -118,9 +118,7 @@ describe Groups::GroupMembersController do
         it 'cannot removes himself from the group' do
           delete :leave, group_id: group
 
-          expect(response).to redirect_to(group_path(group))
-          expect(response).to set_flash[:alert].to "You can not leave the \"#{group.name}\" group. Transfer or delete the group."
-          expect(group.users).to include user
+          expect(response.status).to eq(403)
         end
       end
 
@@ -134,7 +132,7 @@ describe Groups::GroupMembersController do
           delete :leave, group_id: group
 
           expect(response).to set_flash.to 'Your access request to the group has been withdrawn.'
-          expect(response).to redirect_to(dashboard_groups_path)
+          expect(response).to redirect_to(group_path(group))
           expect(group.members.request).to be_empty
           expect(group.users).not_to include user
         end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index fc5f458e79543b3496af06c64b6e6fe380716492..e5e750c855f258c924fd35d4e9bae7fe6fb0e30d 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -171,11 +171,7 @@ describe Projects::ProjectMembersController do
           delete :leave, namespace_id: project.namespace,
                          project_id: project
 
-          expect(response).to redirect_to(
-            namespace_project_path(project.namespace, project)
-          )
-          expect(response).to set_flash[:alert].to "You can not leave the \"#{project.human_name}\" project. Transfer or delete the project."
-          expect(project.users).to include user
+          expect(response.status).to eq(403)
         end
       end
 
@@ -190,7 +186,7 @@ describe Projects::ProjectMembersController do
                          project_id: project
 
           expect(response).to set_flash.to 'Your access request to the project has been withdrawn.'
-          expect(response).to redirect_to(dashboard_projects_path)
+          expect(response).to redirect_to(namespace_project_path(project.namespace, project))
           expect(project.members.request).to be_empty
           expect(project.users).not_to include user
         end
diff --git a/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb b/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..33bf6d3752f465c3ea020a0ac99820c7334a5734
--- /dev/null
+++ b/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+feature 'Groups > Members > Last owner cannot leave group', feature: true do
+  let(:owner) { create(:user) }
+  let(:group) { create(:group) }
+
+  background do
+    group.add_owner(owner)
+    login_as(owner)
+    visit group_path(group)
+  end
+
+  scenario 'user does not see a "Leave Group" link' do
+    expect(page).not_to have_content 'Leave Group'
+  end
+end
diff --git a/spec/features/groups/members/member_leaves_group_spec.rb b/spec/features/groups/members/member_leaves_group_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3185ff924b943af13893c7173881e49400a8f5f5
--- /dev/null
+++ b/spec/features/groups/members/member_leaves_group_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+feature 'Groups > Members > Member leaves group', feature: true do
+  let(:user) { create(:user) }
+  let(:owner) { create(:user) }
+  let(:group) { create(:group, :public) }
+
+  background do
+    group.add_owner(owner)
+    group.add_developer(user)
+    login_as(user)
+    visit group_path(group)
+  end
+
+  scenario 'user leaves group' do
+    click_link 'Leave Group'
+
+    expect(current_path).to eq(dashboard_groups_path)
+    expect(group.users.exists?(user.id)).to be_falsey
+  end
+end
diff --git a/spec/features/groups/members/owner_manages_access_requests_spec.rb b/spec/features/groups/members/owner_manages_access_requests_spec.rb
index 22525ce530b1522dd70ab8b94a857c2b2e593124..321c9bad7d06db4790c3656c1bf138c51a1034fa 100644
--- a/spec/features/groups/members/owner_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/owner_manages_access_requests_spec.rb
@@ -42,7 +42,7 @@ feature 'Groups > Members > Owner manages access requests', feature: true do
 
   def expect_visible_access_request(group, user)
     expect(group.members.request.exists?(user_id: user)).to be_truthy
-    expect(page).to have_content "#{group.name} access requests (1)"
+    expect(page).to have_content "#{group.name} access requests 1"
     expect(page).to have_content user.name
   end
 end
diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb
index a878a96b6ee398e62710d8eddfd1ebd3f1c9c44f..1ea607cbca065b1178ddfc704a20a4243a1925be 100644
--- a/spec/features/groups/members/user_requests_access_spec.rb
+++ b/spec/features/groups/members/user_requests_access_spec.rb
@@ -21,6 +21,7 @@ feature 'Groups > Members > User requests access', feature: true do
     expect(page).to have_content 'Your request for access has been queued for review.'
 
     expect(page).to have_content 'Withdraw Access Request'
+    expect(page).not_to have_content 'Leave Group'
   end
 
   scenario 'user is not listed in the group members page' do
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index 5fe4caa12f07492d1991d041190024debe9578b7..aa2d906fa2e9c09d025aed0629ec5731c1716dc5 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -41,7 +41,7 @@ feature 'Projects > Members > Master manages access requests', feature: true do
 
   def expect_visible_access_request(project, user)
     expect(project.members.request.exists?(user_id: user)).to be_truthy
-    expect(page).to have_content "#{project.name} access requests (1)"
+    expect(page).to have_content "#{project.name} access requests 1"
     expect(page).to have_content user.name
   end
 end
diff --git a/spec/features/projects/members/member_leaves_project_spec.rb b/spec/features/projects/members/member_leaves_project_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..79dec442818be6f4be60d91f36ee52aa7f03de27
--- /dev/null
+++ b/spec/features/projects/members/member_leaves_project_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+feature 'Projects > Members > Member leaves project', feature: true do
+  let(:user) { create(:user) }
+  let(:project) { create(:project) }
+
+  background do
+    project.team << [user, :developer]
+    login_as(user)
+    visit namespace_project_path(project.namespace, project)
+  end
+
+  scenario 'user leaves project' do
+    click_link 'Leave Project'
+
+    expect(current_path).to eq(dashboard_projects_path)
+    expect(project.users.exists?(user.id)).to be_falsey
+  end
+end
diff --git a/spec/features/projects/members/owner_cannot_leave_project_spec.rb b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..67811b1048e6c5df768e7e358a23153158447dde
--- /dev/null
+++ b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+feature 'Projects > Members > Owner cannot leave project', feature: true do
+  let(:owner) { create(:user) }
+  let(:project) { create(:project) }
+
+  background do
+    project.team << [owner, :owner]
+    login_as(owner)
+    visit namespace_project_path(project.namespace, project)
+  end
+
+  scenario 'user does not see a "Leave Project" link' do
+    expect(page).not_to have_content 'Leave Project'
+  end
+end
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index fd92a3a2f0cf20eb61d0dcb6bfd8f5acd305e80a..af420c170ef55aa96d9dcf0da0f690fceab66fd7 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -21,6 +21,7 @@ feature 'Projects > Members > User requests access', feature: true do
     expect(page).to have_content 'Your request for access has been queued for review.'
 
     expect(page).to have_content 'Withdraw Access Request'
+    expect(page).not_to have_content 'Leave Project'
   end
 
   scenario 'user is not listed in the project members page' do
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 9dd0378d165111577c2a650022687150ca008a81..6fa8298d4895b7bd17d34792f4590d50e50ecda9 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -70,22 +70,6 @@ feature 'Project', feature: true do
     end
   end
 
-  describe 'leave project link' do
-    let(:user)    { create(:user) }
-    let(:project) { create(:project, namespace: user.namespace) }
-
-    before do
-      login_with(user)
-      project.team.add_user(user, Gitlab::Access::MASTER)
-      visit namespace_project_path(project.namespace, project)
-    end
-
-    it 'click project-settings and find leave project' do
-      find('#project-settings-button').click
-      expect(page).to have_link('Leave Project')
-    end
-  end
-
   describe 'project title' do
     include WaitForAjax
 
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 3ed3202ac6c5e52821c42a1090b72752e8ceff1d..e9134a3d28324badff2e80058bf16ccd37c87d19 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -134,18 +134,6 @@ describe Member, models: true do
     it { is_expected.to respond_to(:user_email) }
   end
 
-  describe 'Callbacks' do
-    describe 'after_destroy :post_decline_request, if: :request?' do
-      let(:member) { create(:project_member, requested_at: Time.now.utc) }
-
-      it 'calls #post_decline_request' do
-        expect(member).to receive(:post_decline_request)
-
-        member.destroy
-      end
-    end
-  end
-
   describe ".add_user" do
     let!(:user)    { create(:user) }
     let(:project) { create(:project) }
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index eeb74a462acb2b077c0f2d7dc438df81736a143e..18439cac2a4cb62a5d3397ab41a19c60ae5b1168 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -61,16 +61,6 @@ describe GroupMember, models: true do
       end
     end
 
-    describe '#post_decline_request' do
-      it 'calls NotificationService.decline_group_access_request' do
-        member = create(:group_member, user: build_stubbed(:user), requested_at: Time.now)
-
-        expect_any_instance_of(NotificationService).to receive(:decline_group_access_request)
-
-        member.__send__(:post_decline_request)
-      end
-    end
-
     describe '#real_source_type' do
       subject { create(:group_member).real_source_type }
 
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 1e466f9c62045ef243b5b175bb61dc6fbd4ec3eb..bbf65edb27c077386fc12d575e1e6000b27b16a2 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -152,15 +152,5 @@ describe ProjectMember, models: true do
         member.__send__(:after_accept_request)
       end
     end
-
-    describe '#post_decline_request' do
-      it 'calls NotificationService.decline_project_access_request' do
-        member = create(:project_member, user: build_stubbed(:user), requested_at: Time.now)
-
-        expect_any_instance_of(NotificationService).to receive(:decline_project_access_request)
-
-        member.__send__(:post_decline_request)
-      end
-    end
   end
 end
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2395445e7fddcb1372b5010dea71680d712f174b
--- /dev/null
+++ b/spec/services/members/destroy_service_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+
+describe Members::DestroyService, services: true do
+  let(:user) { create(:user) }
+  let(:project) { create(:project) }
+  let!(:member) { create(:project_member, source: project) }
+
+  context 'when member is nil' do
+    before do
+      project.team << [user, :developer]
+    end
+
+    it 'does not destroy the member' do
+      expect { destroy_member(nil, user) }.to raise_error(Gitlab::Access::AccessDeniedError)
+    end
+  end
+
+  context 'when current user cannot destroy the given member' do
+    before do
+      project.team << [user, :developer]
+    end
+
+    it 'does not destroy the member' do
+      expect { destroy_member(member, user) }.to raise_error(Gitlab::Access::AccessDeniedError)
+    end
+  end
+
+  context 'when current user can destroy the given member' do
+    before do
+      project.team << [user, :master]
+    end
+
+    it 'destroys the member' do
+      destroy_member(member, user)
+
+      expect(member).to be_destroyed
+    end
+
+    context 'when the given member is a requester' do
+      before do
+        member.update_column(:requested_at, Time.now)
+      end
+
+      it 'calls Member#after_decline_request' do
+        expect_any_instance_of(NotificationService).to receive(:decline_access_request).with(member)
+
+        destroy_member(member, user)
+      end
+
+      context 'when current user is the member' do
+        it 'does not call Member#after_decline_request' do
+          expect_any_instance_of(NotificationService).not_to receive(:decline_access_request).with(member)
+
+          destroy_member(member, member.user)
+        end
+      end
+
+      context 'when current user is the member and ' do
+        it 'does not call Member#after_decline_request' do
+          expect_any_instance_of(NotificationService).not_to receive(:decline_access_request).with(member)
+
+          destroy_member(member, member.user)
+        end
+      end
+    end
+  end
+
+  def destroy_member(member, user)
+    Members::DestroyService.new(member, user).execute
+  end
+end