diff --git a/.gitignore b/.gitignore
index 7a7b5c9393626f3d0cbc63b0d25ca84ec7a9feae..3e30fb8cf77f62ecf6c9862c501a7566aabb71a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,6 @@ public/assets/
 public/uploads.*
 public/uploads/
 rails_best_practices_output.html
-tags
+/tags
 tmp/
 vendor/bundle/*
diff --git a/CHANGELOG b/CHANGELOG
index 3af83ddc25664d58fdca2675c08d1788e980cf27..be0c005fa061cd2cace7ff310477e77934bbd846 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,7 +1,9 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
 v 7.11.0 (unreleased)
+  - Make Reply-To config apply to change e-mail confirmation and other Devise notifications (Stan Hu)
   - Don't allow a merge request to be merged when its title starts with "WIP".
+  - Add a page title to every page.
   - Get Gitorious importer to work again.
   - Fix clone URL field and X11 Primary selection (Dmitry Medvinsky)
   - Ignore invalid lines in .gitmodules
@@ -11,6 +13,7 @@ v 7.11.0 (unreleased)
   - Fix "Revspec not found" errors when viewing diffs in a forked project with submodules (Stan Hu)
   -
   - Fix broken file browsing with relative submodule in personal projects (Stan Hu)
+  - Fix DB error when trying to tag a repository (Stan Hu)
   - Add "Reply quoting selected text" shortcut key (`r`)
   - Fix bug causing `@whatever` inside an issue's first code block to be picked up as a user mention.
   - Fix bug causing `@whatever` inside an inline code snippet (backtick-style) to be picked up as a user mention.
@@ -21,7 +24,7 @@ v 7.11.0 (unreleased)
   - Include commit comments in MR from a forked project.
   - Fix adding new group members from admin area
   - Add default project and snippet visibility settings to the admin web UI.
-  -
+  - Show incompatible projects in Google Code import status (Stan Hu)
   - Fix bug where commit data would not appear in some subdirectories (Stan Hu)
   - Fix bug where Slack service channel was not saved in admin template settings. (Stan Hu)
   - Move snippets UI to fluid layout
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 9aee3b281f3f78653653d631902c20292607c766..06787ddf874933700fa646a0d78ea54bb3697354 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -8,7 +8,6 @@ class Dispatcher
 
   initPageScripts: ->
     page = $('body').attr('data-page')
-    project_id = $('body').attr('data-project-id')
 
     unless page
       return false
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
index 80190424c1b056de0591b5ac855a78fb94ffbd84..36a9a540747d8aea5f9883ab3a9bb65f66928f8b 100644
--- a/app/assets/stylesheets/generic/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -35,7 +35,12 @@ pre {
   /* Link to current header. */
   h1, h2, h3, h4, h5, h6 {
     position: relative;
-    &:hover > :last-child {
+
+    a.anchor {
+      display: none;
+    }
+
+    &:hover > a.anchor {
       $size: 16px;
       position: absolute;
       right: 100%;
diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb
index fe5456f820caec82e5060fb4e841dade4860e957..56e24386463db3a95e794faf0ed9ed83df635336 100644
--- a/app/controllers/admin/application_controller.rb
+++ b/app/controllers/admin/application_controller.rb
@@ -2,8 +2,8 @@
 #
 # Automatically sets the layout and ensures an administrator is logged in
 class Admin::ApplicationController < ApplicationController
-  layout 'admin'
   before_action :authenticate_admin!
+  layout 'admin'
 
   def authenticate_admin!
     return render_404 unless current_user.is_admin?
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 69fd7901832f1f85093e173408b818c34ad176a3..c9b34eac4b081635567be534bb2dbcd38c3f57ad 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -3,6 +3,7 @@ require 'gon'
 class ApplicationController < ActionController::Base
   include Gitlab::CurrentSettings
   include GitlabRoutingHelper
+  include PageLayoutHelper
 
   PER_PAGE = 20
 
diff --git a/app/controllers/dashboard/application_controller.rb b/app/controllers/dashboard/application_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..962ea38d6c9c01020e2cc888a1b915261032c5af
--- /dev/null
+++ b/app/controllers/dashboard/application_controller.rb
@@ -0,0 +1,3 @@
+class Dashboard::ApplicationController < ApplicationController
+  layout 'dashboard'
+end
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index ed14f4e1f3baa9689c8d000381dc67179560a7f3..3bc94ff218747c10c310952971dd61dbf8be6f99 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,4 +1,4 @@
-class Dashboard::GroupsController < ApplicationController
+class Dashboard::GroupsController < Dashboard::ApplicationController
   def index
     @group_members = current_user.group_members.page(params[:page]).per(PER_PAGE)
   end
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 33227e7f1d8bc6ec1e8e3d352a5310b685ce4814..53896d4f2c73a356334d9e88b36f96b22c3aa71b 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -1,4 +1,4 @@
-class Dashboard::MilestonesController < ApplicationController
+class Dashboard::MilestonesController < Dashboard::ApplicationController
   before_action :load_projects
 
   def index
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 426bc6154153053a82ffa0e54a59e1a22c34c3ac..da96171e88568e7c5bb8ce55e3e350763f58c420 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -1,4 +1,4 @@
-class Dashboard::ProjectsController < ApplicationController
+class Dashboard::ProjectsController < Dashboard::ApplicationController
   before_action :event_filter
 
   def starred
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 40b5de1295a0447005f585e5bc19c9d4bbc37f75..17ddde68f93efc0a4db76e8ebc55114d00094de7 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -1,8 +1,8 @@
-class DashboardController < ApplicationController
-  respond_to :html
-
+class DashboardController < Dashboard::ApplicationController
   before_action :load_projects, except: [:projects]
   before_action :event_filter, only: :show
+  
+  respond_to :html
 
   def show
     @projects = @projects.includes(:namespace)
diff --git a/app/controllers/explore/application_controller.rb b/app/controllers/explore/application_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4b275033d26c76158706ff988d8a9aa4bae5fe2b
--- /dev/null
+++ b/app/controllers/explore/application_controller.rb
@@ -0,0 +1,3 @@
+class Explore::ApplicationController < ApplicationController
+  layout 'explore'
+end
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index a7250b799f352141d412f47efe062dd0d2a4fb6a..55cda0cff17bada4f7cbc701bf1c4b7aba27b971 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -1,9 +1,7 @@
-class Explore::GroupsController < ApplicationController
+class Explore::GroupsController < Explore::ApplicationController
   skip_before_action :authenticate_user!,
                      :reject_blocked, :set_current_user_for_observers
 
-  layout "explore"
-
   def index
     @groups = GroupsFinder.new.execute(current_user)
     @groups = @groups.search(params[:search]) if params[:search].present?
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index b1b0a2514dc0858ff30ffcb74dd10ec1e2e6232f..e9bcb44f6b3967489443f96cc477a7560890217f 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -1,9 +1,7 @@
-class Explore::ProjectsController < ApplicationController
+class Explore::ProjectsController < Explore::ApplicationController
   skip_before_action :authenticate_user!,
                      :reject_blocked
 
-  layout 'explore'
-
   def index
     @projects = ProjectsFinder.new.execute(current_user)
     @tags = @projects.tags_on(:tags)
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index 469a6813ee22a5e80f262705b3ed7208b3964c4c..4df9d1b7533c2d229ca4b50d1b9d06664b57ea01 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -1,4 +1,5 @@
 class Groups::ApplicationController < ApplicationController
+  layout 'group'
 
   private
   
@@ -17,12 +18,4 @@ class Groups::ApplicationController < ApplicationController
       return render_404
     end
   end
-
-  def determine_layout
-    if current_user
-      'group'
-    else
-      'public_group'
-    end
-  end
 end
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index 38071410f404a12b4b40ace7b181f6a5b12bb0fd..6aa64222f7789601cfbfbca55ed02ee5b69d8cb1 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -1,6 +1,4 @@
 class Groups::AvatarsController < ApplicationController
-  layout "profile"
-
   def destroy
     @group = Group.find_by(path: params[:group_id])
     @group.remove_avatar!
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 5648a652e8e8a5da4e4f0c8891695c0e48e1077a..a11c554a2af43ec2da0ab679a8db8a298c59ffae 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -6,8 +6,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
   before_action :authorize_read_group!
   before_action :authorize_admin_group!, except: [:index, :leave]
 
-  layout :determine_layout
-
   def index
     @project = @group.projects.find(params[:project_id]) if params[:project_id]
     @members = @group.group_members
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 41564b04a92482bd297e042d4e9029daf8aab23a..669f7f3126df188bb34056a3cd7246506e5f9b1a 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -1,6 +1,4 @@
-class Groups::MilestonesController < ApplicationController
-  layout 'group'
-
+class Groups::MilestonesController < Groups::ApplicationController
   before_action :authorize_group_milestone!, only: :update
 
   def index
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 294af0b170450ef6a198a0b11e00bf4bd4813aed..34f0b257db3b48efc784c39e0a4b177b3b5c4450 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -11,7 +11,6 @@ class GroupsController < Groups::ApplicationController
   # Load group projects
   before_action :load_projects, except: [:new, :create, :projects, :edit, :update]
   before_action :event_filter, only: :show
-  before_action :set_title, only: [:new, :create]
 
   layout :determine_layout
 
@@ -119,17 +118,11 @@ class GroupsController < Groups::ApplicationController
     end
   end
 
-  def set_title
-    @title = 'New Group'
-  end
-
   def determine_layout
     if [:new, :create].include?(action_name.to_sym)
-      'navless'
-    elsif current_user
-      'group'
+      'application'
     else
-      'public_group'
+      'group'
     end
   end
 
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index 35ece5b270b95c119292704452884f888e1ad650..8a45dc8860d15f20b40299edd95e90186fc482f7 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -1,14 +1,16 @@
 class HelpController < ApplicationController
+  layout 'help'
+
   def index
   end
 
   def show
-    category = clean_path_info(path_params[:category])
-    file = path_params[:file]
+    @category = clean_path_info(path_params[:category])
+    @file = path_params[:file]
 
     respond_to do |format|
       format.any(:markdown, :md, :html) do
-        path = Rails.root.join('doc', category, "#{file}.md")
+        path = Rails.root.join('doc', @category, "#{@file}.md")
 
         if File.exist?(path)
           @markdown = File.read(path)
@@ -22,7 +24,7 @@ class HelpController < ApplicationController
 
       # Allow access to images in the doc folder
       format.any(:png, :gif, :jpeg) do
-        path = Rails.root.join('doc', category, "#{file}.#{params[:format]}")
+        path = Rails.root.join('doc', @category, "#{@file}.#{params[:format]}")
 
         if File.exist?(path)
           send_file(path, disposition: 'inline')
diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb
index 5adf6ed7853d655e471a590c5a7f394b28d45c30..4aa6d28c9a803a9e875123b3ea3edcc3f1e64e9b 100644
--- a/app/controllers/import/google_code_controller.rb
+++ b/app/controllers/import/google_code_controller.rb
@@ -72,6 +72,7 @@ class Import::GoogleCodeController < Import::BaseController
     end
 
     @repos = client.repos
+    @incompatible_repos = client.incompatible_repos
 
     @already_added_projects = current_user.created_projects.where(import_type: "google_code")
     already_added_projects_names = @already_added_projects.pluck(:import_source)
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index a29c03395f42ca43a94cf61d7a473efc32500c87..eb3c82335308692bf86d90b424d7167313d75be2 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -4,8 +4,6 @@ class InvitesController < ApplicationController
 
   respond_to :html
 
-  layout 'navless'
-
   def show
 
   end
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index ea256de2c3e53ce6f59b4745fb29a906ef27d97f..507b8290a2b079861eb0143719dcbcd683908a3c 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -1,6 +1,9 @@
 class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
+  include PageLayoutHelper
+  
   before_action :authenticate_user!
-  layout "profile"
+
+  layout 'profile'
 
   def index
     head :forbidden and return
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index 6d3c1a320db1d0ca1ee30e86ada197c4c8c89ff9..24025d8c723f27146a4676685071024e19029903 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -1,6 +1,7 @@
 class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
   before_action :authenticate_resource_owner!
-  layout "profile"
+
+  layout 'profile'
 
   def new
     if pre_auth.authorizable?
diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb
index 0b27ce7da7291db743126bae189f107e948652d5..3ab6def511c8b3ca54c3d1c70b32c2ad09a89495 100644
--- a/app/controllers/oauth/authorized_applications_controller.rb
+++ b/app/controllers/oauth/authorized_applications_controller.rb
@@ -1,5 +1,7 @@
 class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
-  layout "profile"
+  include PageLayoutHelper
+
+  layout 'profile'
 
   def destroy
     Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner)
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index 9bd34fe22613ea5401ab2408887a3e277414ef47..175afbf84259fa09434ce6da423a14d90d7a9b3f 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -1,6 +1,4 @@
-class Profiles::AccountsController < ApplicationController
-  layout "profile"
-
+class Profiles::AccountsController < Profiles::ApplicationController
   def show
     @user = current_user
   end
diff --git a/app/controllers/profiles/application_controller.rb b/app/controllers/profiles/application_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c8be288b9a077ec86c880496a5640baa82ffb5b5
--- /dev/null
+++ b/app/controllers/profiles/application_controller.rb
@@ -0,0 +1,3 @@
+class Profiles::ApplicationController < ApplicationController
+  layout 'profile'
+end
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
index 57f3bbf0627ad4d79fe4e712b2f8504ec5c3cf46..f193adb46b488734c3e7c294823714ac92062f6e 100644
--- a/app/controllers/profiles/avatars_controller.rb
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -1,6 +1,4 @@
-class Profiles::AvatarsController < ApplicationController
-  layout "profile"
-
+class Profiles::AvatarsController < Profiles::ApplicationController
   def destroy
     @user = current_user
     @user.remove_avatar!
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index 954c98c0d9ff6775ba75fa3d9d9090a2787088de..3e904700de5fb9607660ea952d1e0e4d94b145a4 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -1,6 +1,4 @@
-class Profiles::EmailsController < ApplicationController
-  layout "profile"
-
+class Profiles::EmailsController < Profiles::ApplicationController
   def index
     @primary = current_user.email
     @public_email = current_user.public_email
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index b0a5a631c63e3282a4aed088b7ad2749a7ad7581..f3224148fda5b478a9091e3671b45c65dfc6c3e3 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -1,5 +1,4 @@
-class Profiles::KeysController < ApplicationController
-  layout "profile"
+class Profiles::KeysController < Profiles::ApplicationController
   skip_before_action :authenticate_user!, only: [:get_keys]
 
   def index
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 3fdcbbab61b531633827a36f336393e9d185f137..22423651c1747c9928f32999f009fc78f7c0cf1f 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -1,6 +1,4 @@
-class Profiles::NotificationsController < ApplicationController
-  layout 'profile'
-
+class Profiles::NotificationsController < Profiles::ApplicationController
   def show
     @user = current_user
     @notification = current_user.notification
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
index b719a7fe9a9ca495c522542243ce653fb2056637..c780e0983f93ae07443138cb2a42ef95828a767b 100644
--- a/app/controllers/profiles/passwords_controller.rb
+++ b/app/controllers/profiles/passwords_controller.rb
@@ -1,12 +1,11 @@
-class Profiles::PasswordsController < ApplicationController
-  layout :determine_layout
-
+class Profiles::PasswordsController < Profiles::ApplicationController
   skip_before_action :check_password_expiration, only: [:new, :create]
 
   before_action :set_user
-  before_action :set_title
   before_action :authorize_change_password!
 
+  layout :determine_layout
+
   def new
   end
 
@@ -66,13 +65,9 @@ class Profiles::PasswordsController < ApplicationController
     @user = current_user
   end
 
-  def set_title
-    @title = "New password"
-  end
-
   def determine_layout
     if [:new, :create].include?(action_name.to_sym)
-      'navless'
+      'application'
     else
       'profile'
     end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index eb001e8d73955ed5b8028e06e34cc3c5b74b06e5..f4366c18e7be0be077632b107d43154a9d912d94 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -1,12 +1,10 @@
-class ProfilesController < ApplicationController
+class ProfilesController < Profiles::ApplicationController
   include ActionView::Helpers::SanitizeHelper
 
   before_action :user
   before_action :authorize_change_username!, only: :update_username
   skip_before_action :require_email, only: [:show, :update]
 
-  layout 'profile'
-
   def show
   end
 
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index f7a28e920d190d139444d713509e04396ff4af85..ee88d49b40036a6fa125e74dcd0dd1a6f3bd048a 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -1,7 +1,7 @@
 class Projects::ApplicationController < ApplicationController
   before_action :project
   before_action :repository
-  layout :determine_layout
+  layout 'project'
 
   def authenticate_user!
     # Restrict access to Projects area only
@@ -17,14 +17,6 @@ class Projects::ApplicationController < ApplicationController
     super
   end
 
-  def determine_layout
-    if current_user
-      'projects'
-    else
-      'public_projects'
-    end
-  end
-
   def require_branch_head
     unless @repository.branch_names.include?(@ref)
       redirect_to(
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index 22a12c4b9ae025e4d4d2f6ebd495e3648095c0f3..9c3763d5934e0245cc7e8133b2e136fdd7510fa1 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -1,6 +1,4 @@
 class Projects::AvatarsController < Projects::ApplicationController
-  layout 'project'
-
   before_action :project
 
   def show
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index 01a079d2e6593d9a3bdb6e05d5d9bd3778d655e7..9e72597ea87f3296183ab325ab22e64637e3de23 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -18,7 +18,6 @@ class Projects::ForksController < Projects::ApplicationController
         notice: 'Project was successfully forked.'
       )
     else
-      @title = 'Fork project'
       render :error
     end
   end
diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb
index e2d0b0d945908c9746c7a94e4de8422b98ea675a..71ecc20dd95ea7fe681e5b9ed5ef378974eeb8e6 100644
--- a/app/controllers/projects/uploads_controller.rb
+++ b/app/controllers/projects/uploads_controller.rb
@@ -1,6 +1,4 @@
 class Projects::UploadsController < Projects::ApplicationController
-  layout 'project'
-
   skip_before_action :authenticate_user!, :reject_blocked!, :project,
     :repository, if: -> { action_name == 'show' && image? }
 
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 883e5865a21060a040d6d8ec5b6e2f7b98ec2293..dc4303515510a7767e8ba219ae7db53eedf88e4f 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -6,17 +6,16 @@ class ProjectsController < ApplicationController
 
   # Authorize
   before_action :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
-  before_action :set_title, only: [:new, :create]
   before_action :event_filter, only: :show
 
-  layout 'navless', only: [:new, :create, :fork]
+  layout :determine_layout
 
   def new
     @project = Project.new
   end
 
   def edit
-    render 'edit', layout: 'project_settings'
+    render 'edit'
   end
 
   def create
@@ -46,7 +45,7 @@ class ProjectsController < ApplicationController
         end
         format.js
       else
-        format.html { render 'edit', layout: 'project_settings' }
+        format.html { render 'edit' }
         format.js
       end
     end
@@ -72,13 +71,13 @@ class ProjectsController < ApplicationController
       format.html do
         if @project.repository_exists?
           if @project.empty_repo?
-            render 'projects/empty', layout: user_layout
+            render 'projects/empty'
           else
             @last_push = current_user.recent_push(@project.id) if current_user
-            render :show, layout: user_layout
+            render :show
           end
         else
-          render 'projects/no_repo', layout: user_layout
+          render 'projects/no_repo'
         end
       end
 
@@ -160,12 +159,14 @@ class ProjectsController < ApplicationController
 
   private
 
-  def set_title
-    @title = 'New Project'
-  end
-
-  def user_layout
-    current_user ? 'projects' : 'public_projects'
+  def determine_layout
+    if [:new, :create].include?(action_name.to_sym)
+      'application'
+    elsif [:edit, :update].include?(action_name.to_sym)
+      'project_settings'
+    else
+      'project'
+    end
   end
 
   def load_events
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index ad9e9e8487e75004ad3f73fd1bac9e68cf08081c..4e2ea6c5710ff14cca8c75d7f5f764d7b3410cc3 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -1,6 +1,8 @@
 class SearchController < ApplicationController
   include SearchHelper
 
+  layout 'search'
+
   def show
     return if params[:search].nil? || params[:search].blank?
 
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index a5259466cb8d2b6853b07c27375cbb9e8235235c..cf672c5c0932b05b8a34d20aaed6e8a09460c98f 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -7,14 +7,11 @@ class SnippetsController < ApplicationController
   # Allow destroy snippet
   before_action :authorize_admin_snippet!, only: [:destroy]
 
-  before_action :set_title
-
   skip_before_action :authenticate_user!, only: [:index, :user_index, :show, :raw]
 
+  layout 'snippets'
   respond_to :html
 
-  layout :determine_layout
-
   def index
     if params[:username].present?
       @user = User.find_by(username: params[:username])
@@ -98,16 +95,7 @@ class SnippetsController < ApplicationController
     return render_404 unless can?(current_user, :admin_personal_snippet, @snippet)
   end
 
-  def set_title
-    @title = 'Snippets'
-    @title_url = snippets_path
-  end
-
   def snippet_params
     params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level)
   end
-
-  def determine_layout
-    current_user ? 'snippets' : 'public_users'
-  end
 end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 460cc868b35cee1b7aef1942d4dda40408fc9779..2bb5c338cf60fc119b3083a8e4eb08d902a79a3c 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,7 +1,6 @@
 class UsersController < ApplicationController
   skip_before_action :authenticate_user!
   before_action :set_user
-  layout :determine_layout
 
   def show
     @contributed_projects = contributed_projects.joined(@user).
@@ -13,9 +12,6 @@ class UsersController < ApplicationController
     # Collect only groups common for both users
     @groups = @user.groups & GroupsFinder.new.execute(current_user)
 
-    @title = @user.name
-    @title_url = user_path(@user)
-
     respond_to do |format|
       format.html
 
@@ -51,14 +47,6 @@ class UsersController < ApplicationController
     render 'calendar_activities', layout: false
   end
 
-  def determine_layout
-    if current_user
-      'navless'
-    else
-      'public_users'
-    end
-  end
-
   private
 
   def set_user
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index bc234500000458ed94d118bee01b959c0514f5c2..24263a0f619e45bbba0fa44bff7b88ffe1fbb137 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -34,10 +34,8 @@ module GitlabMarkdownHelper
 
       # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch
       rend = Redcarpet::Render::GitlabHTML.new(self, user_color_scheme_class, {
-        with_toc_data:   true,
-        safe_links_only: true,
-        # Handled further down the line by HTML::Pipeline::SanitizationFilter
-        escape_html:     false
+        # Handled further down the line by Gitlab::Markdown::SanitizationFilter
+        escape_html: false
       }.merge(options))
 
       # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
@@ -45,7 +43,6 @@ module GitlabMarkdownHelper
         no_intra_emphasis:   true,
         tables:              true,
         fenced_code_blocks:  true,
-        autolink:            true,
         strikethrough:       true,
         lax_spacing:         true,
         space_after_headers: true,
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index add0a776a6366885eefac034113589c891d2eb5b..3569ac2af635537c852a8d110ae7a42cf5597c79 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -19,24 +19,6 @@ module GroupsHelper
     end
   end
 
-  def group_head_title
-    title = @group.name
-
-    title = if current_action?(:issues)
-              "Issues - " + title
-            elsif current_action?(:merge_requests)
-              "Merge requests - " + title
-            elsif current_action?(:members)
-              "Members - " + title
-            elsif current_action?(:edit)
-              "Settings - " + title
-            else
-              title
-            end
-
-    title
-  end
-
   def group_settings_page?
     if current_controller?('groups')
       current_action?('edit') || current_action?('projects')
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index c3b4731dff303616d9689ca84e3d0686d0d5e425..36d3f371c1b69408a36df65180523ef104780dd8 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -43,17 +43,6 @@ module IssuesHelper
     end
   end
 
-  def title_for_issue(issue_iid, project = @project)
-    return '' if project.nil?
-
-    if project.default_issues_tracker?
-      issue = project.issues.where(iid: issue_iid).first
-      return issue.title if issue
-    end
-
-    ''
-  end
-
   def issue_timestamp(issue)
     # Shows the created at time and the updated at time if different
     ts = "#{time_ago_with_tooltip(issue.created_at, 'bottom', 'note_created_ago')}"
@@ -110,5 +99,5 @@ module IssuesHelper
   end
 
   # Required for Gitlab::Markdown::IssueReferenceFilter
-  module_function :url_for_issue, :title_for_issue
+  module_function :url_for_issue
 end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..01b6a63552c1c00123becdcc5a98c19ee9e7374b
--- /dev/null
+++ b/app/helpers/page_layout_helper.rb
@@ -0,0 +1,26 @@
+module PageLayoutHelper
+  def page_title(*titles)
+    @page_title ||= []
+
+    @page_title.push(*titles.compact) if titles.any?
+
+    @page_title.join(" | ")
+  end
+
+  def header_title(title = nil, title_url = nil)
+    if title
+      @header_title     = title
+      @header_title_url = title_url
+    else
+      @header_title_url ? link_to(@header_title, @header_title_url) : @header_title
+    end
+  end
+
+  def sidebar(name = nil)
+    if name
+      @sidebar = name
+    else
+      @sidebar
+    end
+  end
+end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index c2a7732e6f03dddcc2e2601dc17127088eedbad4..96d2606f1a1ae765492ac37fdf199cd7235ada0d 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -192,46 +192,6 @@ module ProjectsHelper
     'unknown'
   end
 
-  def project_head_title
-    title = @project.name_with_namespace
-
-    title = if current_controller?(:tree)
-              "#{@project.path}\/#{@path} at #{@ref} - " + title
-            elsif current_controller?(:issues)
-              if current_action?(:show)
-                "Issue ##{@issue.iid} - #{@issue.title} - " + title
-              else
-                "Issues - " + title
-              end
-            elsif current_controller?(:blob)
-              if current_action?(:new) || current_action?(:create)
-                "New file at #{@ref}"
-              elsif current_action?(:show)
-                "#{@blob.path} at #{@ref}"
-              elsif @blob
-                "Edit file #{@blob.path} at #{@ref}"
-              end
-            elsif current_controller?(:commits)
-              "Commits at #{@ref} - " + title
-            elsif current_controller?(:merge_requests)
-              if current_action?(:show)
-                "Merge request ##{@merge_request.iid} - " + title
-              else
-                "Merge requests - " + title
-              end
-            elsif current_controller?(:wikis)
-              "Wiki - " + title
-            elsif current_controller?(:network)
-              "Network graph - " + title
-            elsif current_controller?(:graphs)
-              "Graphs - " + title
-            else
-              title
-            end
-
-    title
-  end
-
   def default_url_to_repo(project = nil)
     project = project || @project
     current_user ? project.url_to_repo : project.http_url_to_repo
diff --git a/app/mailers/devise_mailer.rb b/app/mailers/devise_mailer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5395fe16414f087f048cfa1d2c36dafa57e31f28
--- /dev/null
+++ b/app/mailers/devise_mailer.rb
@@ -0,0 +1,4 @@
+class DeviseMailer < Devise::Mailer
+  default from: "GitLab <#{Gitlab.config.gitlab.email_from}>"
+  default reply_to: Gitlab.config.gitlab.email_reply_to
+end
diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb
index 50efcb32f1b9f93c41dd20e858a9405e97e4543a..85fdb12bfdc7a332d84cc90992ded77a6e027a72 100644
--- a/app/models/external_issue.rb
+++ b/app/models/external_issue.rb
@@ -15,6 +15,10 @@ class ExternalIssue
     @issue_identifier.to_s
   end
 
+  def title
+    "External Issue #{self}"
+  end
+
   def ==(other)
     other.is_a?(self.class) && (to_s == other.to_s)
   end
diff --git a/app/models/project.rb b/app/models/project.rb
index 397232e98d8a46c0d8ebf57e5ad86b0c2f04a307..e866681aab947e736a0f071f0bd3fccd87bbbe94 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -329,14 +329,18 @@ class Project < ActiveRecord::Base
     self.id
   end
 
-  def issue_exists?(issue_id)
+  def get_issue(issue_id)
     if default_issues_tracker?
-      self.issues.where(iid: issue_id).first.present?
+      issues.find_by(iid: issue_id)
     else
-      true
+      ExternalIssue.new(issue_id, self)
     end
   end
 
+  def issue_exists?(issue_id)
+    get_issue(issue_id)
+  end
+
   def default_issue_tracker
     gitlab_issue_tracker_service || create_gitlab_issue_tracker_service
   end
@@ -350,11 +354,7 @@ class Project < ActiveRecord::Base
   end
 
   def default_issues_tracker?
-    if external_issue_tracker
-      false
-    else
-      true
-    end
+    !external_issue_tracker
   end
 
   def external_issues_trackers
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
index 39b66647a5a878754f9afc1ac642ab387d9c91db..1632dd8affa286d298fbe94f1587b3d86920164f 100644
--- a/app/views/admin/application_settings/show.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Settings"
 %h3.page-title Application settings
 %hr
 = render 'form'
diff --git a/app/views/admin/applications/edit.html.haml b/app/views/admin/applications/edit.html.haml
index e408ae2f29d0647bae76bf1c039e33ec525ff34a..c596866bde210980094f6bc80ce6440e350dfdcf 100644
--- a/app/views/admin/applications/edit.html.haml
+++ b/app/views/admin/applications/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @application.name, "Applications"
 %h3.page-title Edit application
 - @url = admin_application_path(@application)
-= render 'form', application: @application
\ No newline at end of file
+= render 'form', application: @application
diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml
index d550278710ebeaf8555308eb868cf9e34f83d55b..fc921a966f3fe7f139ecfb04c9aaf75c97ce259e 100644
--- a/app/views/admin/applications/index.html.haml
+++ b/app/views/admin/applications/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Applications"
 %h3.page-title
   System OAuth applications
 %p.light
diff --git a/app/views/admin/applications/new.html.haml b/app/views/admin/applications/new.html.haml
index 7c62425f19c94bd3bee5c05d9ed9a944b0a2b1a4..6310d89bd6bc5ee05a526d4871f804a65e4e2534 100644
--- a/app/views/admin/applications/new.html.haml
+++ b/app/views/admin/applications/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Application"
 %h3.page-title New application
 - @url = admin_applications_path
-= render 'form', application: @application
\ No newline at end of file
+= render 'form', application: @application
diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml
index 2abe390ce13e7b853e11d7c6f28766375dd2c1d6..0ea2ffeda9976b5909d9631b0f397dc1b4b5c9cb 100644
--- a/app/views/admin/applications/show.html.haml
+++ b/app/views/admin/applications/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @application.name, "Applications"
 %h3.page-title
   Application: #{@application.name}
 
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index 4ef8e878a7f1c419f8c89c641842d4739a46a17e..3a01e1151098e8bc3d553ae2e6e9f68a74f428b5 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Background Jobs"
 %h3.page-title Background Jobs
 %p.light GitLab uses #{link_to "sidekiq", "http://sidekiq.org/"} library for async job processing
 
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
index 7e29311bf42c6f8d7cf8a6f18f362a11af4ef902..267c9a5292104e39f341fceca1e64c8ccf148c19 100644
--- a/app/views/admin/broadcast_messages/index.html.haml
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Broadcast Messages"
 %h3.page-title
   Broadcast Messages
 %p.light
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 2ae83ab95f7ffe7df832ce3bf3d5a6b7e02964ed..367d25cd6a1a975902fa6bbb29ff290e757297c7 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Deploy Keys"
 .panel.panel-default
   .panel-heading
     Public deploy keys (#{@deploy_keys.count})
diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml
index c00049424c58180927c409f7b7dd7fd1317f5e34..5b46b3222a9cc5a5ffcdf21c72451a0f739f39be 100644
--- a/app/views/admin/deploy_keys/new.html.haml
+++ b/app/views/admin/deploy_keys/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Deploy Key"
 %h3.page-title New public deploy key
 %hr
 
diff --git a/app/views/admin/deploy_keys/show.html.haml b/app/views/admin/deploy_keys/show.html.haml
index cfa2adf92eefb0ab6bf5f9bb8951600768a7d8a4..ea361ca4bdbc471af88e3dcb74643d9b16f35bf4 100644
--- a/app/views/admin/deploy_keys/show.html.haml
+++ b/app/views/admin/deploy_keys/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @deploy_key.title, "Deploy Keys"
 .row
   .col-md-4
     .panel.panel-default
diff --git a/app/views/admin/groups/edit.html.haml b/app/views/admin/groups/edit.html.haml
index 824e51c1cf1340ca4ab2244ea376600257ff4f83..eb09a6328ed39d16d1a2a355899b848c4ac75633 100644
--- a/app/views/admin/groups/edit.html.haml
+++ b/app/views/admin/groups/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @group.name, "Groups"
 %h3.page-title Edit group: #{@group.name}
 %hr
 = render 'form'
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 4c53ff55708790a7d5e5a9e47df5c3fe91086668..e00b23ad99f5fcea21433abbb73867822a47881d 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Groups"
 %h3.page-title
   Groups (#{@groups.total_count})
   = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right"
diff --git a/app/views/admin/groups/new.html.haml b/app/views/admin/groups/new.html.haml
index f46f45c5514853c563a4515d52bbda61ba975a41..c81ee552ac3717ecaed1818006273002e80899cf 100644
--- a/app/views/admin/groups/new.html.haml
+++ b/app/views/admin/groups/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Group"
 %h3.page-title New group
 %hr
 = render 'form'
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 427f38018b0b67a9244214b3bfb21af5d52c506d..187314872de54c851e4cff2f5c287edcac017863 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @group.name, "Groups"
 %h3.page-title
   Group: #{@group.name}
 
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index 7a9dc113f2a93df806c6aa1d3c6b28c69020f8e1..e74e1e85f4165251dc1f8f7d1cf988c01584397c 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "System Hooks"
 %h3.page-title
   System hooks
 
diff --git a/app/views/admin/keys/show.html.haml b/app/views/admin/keys/show.html.haml
index 5b23027b3abdbee6c68b0589bf846054c37a5b5e..9ee77c77398bb0d59a79514e04a76efd10c23958 100644
--- a/app/views/admin/keys/show.html.haml
+++ b/app/views/admin/keys/show.html.haml
@@ -1 +1,2 @@
+- page_title @key.title, "Keys"
 = render "profiles/keys/key_details", admin: true
diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml
index 384c6ee9af5e4fe063a5b32d23946d809fec79c1..1484baa78e0aad795d536599e42fe7ef2f90bcd3 100644
--- a/app/views/admin/logs/show.html.haml
+++ b/app/views/admin/logs/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Logs"
 - loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
              Gitlab::ProductionLogger, Gitlab::SidekiqLogger]
 %ul.nav.nav-tabs.log-tabs
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 0a13791029d4662a5e69326b51b78078f4b64590..f43d46356fa4c85524499343bed5b5e10f8e095a 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Projects"
 = render 'shared/show_aside'
 
 .row
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 78684c692c7e1468b5dc85c81622e2fc9ce8b7ed..4c2865ac3f223d1e946feadbeec82fef1cf12ba2 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @project.name_with_namespace, "Projects"
 %h3.page-title
   Project: #{@project.name_with_namespace}
   = link_to edit_project_path(@project), class: "btn pull-right" do
diff --git a/app/views/admin/services/edit.html.haml b/app/views/admin/services/edit.html.haml
index bcc5832792fea10b70d29a9fb15633f06e001dd0..53d970e33c175c104d06f94d45ecee3ba8c36adf 100644
--- a/app/views/admin/services/edit.html.haml
+++ b/app/views/admin/services/edit.html.haml
@@ -1 +1,2 @@
+- page_title @service.title, "Service Templates"
 = render 'form'
diff --git a/app/views/admin/services/index.html.haml b/app/views/admin/services/index.html.haml
index 0093fb97765b7aa30666f1540a3c8ff74688eb4f..e2377291142f1273697a258e0e350e621f003543 100644
--- a/app/views/admin/services/index.html.haml
+++ b/app/views/admin/services/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Service Templates"
 %h3.page-title Service templates
 %p.light Service template allows you to set default values for project services
 
diff --git a/app/views/admin/users/edit.html.haml b/app/views/admin/users/edit.html.haml
index d71d8189c51274626eb34781fc8ecc462964f0a7..a8837d74dd97043adb8300c71d37f04190e892a8 100644
--- a/app/views/admin/users/edit.html.haml
+++ b/app/views/admin/users/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @user.name, "Users"
 %h3.page-title
   Edit user: #{@user.name}
 .back-link
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 23a9d76963956e025e0fa6803b94b2222bf8fcf5..fe6484702333c52873e4217bb81ec7e455922413 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Users"
 = render 'shared/show_aside'
 
 .row
diff --git a/app/views/admin/users/new.html.haml b/app/views/admin/users/new.html.haml
index 8fbb757f42455c1673a7d25681ee513c07740f62..bfc36ed7373dd998f8990fdd37de4ec585b2c4ac 100644
--- a/app/views/admin/users/new.html.haml
+++ b/app/views/admin/users/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New User"
 %h3.page-title
   New user
 %hr
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 3524f04c5ed5862b12adffb43dfec1f507a8a9fb..7fc85206109d2472d77e8a9c84de192544e8e954 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @user.name, "Users"
 %h3.page-title
   User:
   = @user.name
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 0cb7f764fab43acc98833a0a3e5381ef3ad9a4ad..5ecd53cff8443c00f3589107336b6a59d70ed2f1 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Groups"
 %h3.page-title
   Group Membership
   - if current_user.can_create_group?
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 62cc80a30dc866d706c7468df14a816c63e5cb2b..dfdf0d68c8f426f4e04c12bec9d1ef22664e2419 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -1,3 +1,4 @@
+- page_title "Issues"
 = content_for :meta_tags do
   - if current_user
     = auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 97a42461b4e0f54d32a91c139bf1ac3818f1f46b..a7e1b08a0a4e38a6d5065026a662e7ceab96d572 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -1,3 +1,4 @@
+- page_title "Merge Requests"
 %h3.page-title
   Merge Requests
 
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index 9944c0df8152abe335797699479d9e1175b3e9bf..9a9a5e139a44f72333c0043cd3b615f2d7dcace0 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Milestones"
 %h3.page-title
   Milestones
   %span.pull-right #{@dashboard_milestones.count} milestones
diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml
index 57cce9ab749ec85fe3e65118a0864c01311c4b72..24f0bcb60d5c9d29ffd8bca42ab30644b67089cb 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @dashboard_milestone.title, "Milestones"
 %h4.page-title
   .issue-box{ class: "issue-box-#{@dashboard_milestone.closed? ? 'closed' : 'open'}" }
     - if @dashboard_milestone.closed?
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index 67943f2267b2037c2b9addbf7f646ccc36b00a4e..8aaa0a7f071582794bbe9fbcc2179b7bcedd8829 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -1,3 +1,4 @@
+- page_title "Starred Projects"
 - if @projects.any?
   = render 'shared/show_aside'
 
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index d3e37f7494c99a4455b05e95bf2aa4804a307dde..42cfbbf84f2b731d4f16e1a5c8f79820149cac0d 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Sign up"
 = render 'devise/shared/signup_box'
 
-= render 'devise/shared/sign_in_link'
\ No newline at end of file
+= render 'devise/shared/sign_in_link'
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index 89e4e229ac0dd3d1b5c91d65b03f037a23cc79ec..dbc8eda61961b8cae837da916870e0a67d47f51f 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Sign in"
 %div
   - if signin_enabled? || ldap_enabled?
     = render 'devise/shared/signin_box'
diff --git a/app/views/doorkeeper/applications/edit.html.haml b/app/views/doorkeeper/applications/edit.html.haml
index 61584eb9c498af026e0e0f97767639830a5c3555..fb6aa30acee484c715027673468ff9b1f3083a3c 100644
--- a/app/views/doorkeeper/applications/edit.html.haml
+++ b/app/views/doorkeeper/applications/edit.html.haml
@@ -1,2 +1,3 @@
+- page_title "Edit", @application.name, "Applications"
 %h3.page-title Edit application
-= render 'form', application: @application
\ No newline at end of file
+= render 'form', application: @application
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index e5be4b4bcac03d41252ecc84df4bc7ee5ce6f577..3b0b19107ca53612bb61fced036d43bea48b1b4f 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Applications"
 %h3.page-title Your applications
 %p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
 %table.table.table-striped
@@ -13,4 +14,4 @@
         %td= link_to application.name, oauth_application_path(application)
         %td= application.redirect_uri
         %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link'
-        %td= render 'delete_form', application: application
\ No newline at end of file
+        %td= render 'delete_form', application: application
diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml
index 82e78b4af132472bfe0ba411d4e67b25b5781baf..80340aca54cecfd832202359055c59ad7821a680 100644
--- a/app/views/doorkeeper/applications/show.html.haml
+++ b/app/views/doorkeeper/applications/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @application.name, "Applications"
 %h3.page-title
   Application: #{@application.name}
 
diff --git a/app/views/errors/access_denied.html.haml b/app/views/errors/access_denied.html.haml
index a1d8664c4ce216ee03702560d9809d58b7e27bb6..012e9857642ca898cec37e0f05b4f14e78fedf52 100644
--- a/app/views/errors/access_denied.html.haml
+++ b/app/views/errors/access_denied.html.haml
@@ -1,3 +1,4 @@
+- page_title "Access Denied"
 %h1 403
 %h3 Access Denied
 %hr
diff --git a/app/views/errors/encoding.html.haml b/app/views/errors/encoding.html.haml
index 64c7451a8dabba140c03f46dd6190760b0f24510..90cfbebfcc67212f86b7c0ad27407c3c5051eea0 100644
--- a/app/views/errors/encoding.html.haml
+++ b/app/views/errors/encoding.html.haml
@@ -1,3 +1,4 @@
+- page_title "Encoding Error"
 %h1 500
 %h3 Encoding Error
 %hr
diff --git a/app/views/errors/git_not_found.html.haml b/app/views/errors/git_not_found.html.haml
index 189e53bca55b7ae44d642591a49d646c63927882..ff5d4cc1506f57a541c6990e9b33d28cda777e7e 100644
--- a/app/views/errors/git_not_found.html.haml
+++ b/app/views/errors/git_not_found.html.haml
@@ -1,3 +1,4 @@
+- page_title "Git Resource Not Found"
 %h1 404
 %h3 Git Resource Not found
 %hr
diff --git a/app/views/errors/not_found.html.haml b/app/views/errors/not_found.html.haml
index 7bf88f592cf621779b42b1198bdb62354db0a7f1..3756b98ebb2e59148b3c91b38212e3259e72e714 100644
--- a/app/views/errors/not_found.html.haml
+++ b/app/views/errors/not_found.html.haml
@@ -1,3 +1,4 @@
+- page_title "Not Found"
 %h1 404
 %h3 The resource you were looking for doesn't exist.
 %hr
diff --git a/app/views/errors/omniauth_error.html.haml b/app/views/errors/omniauth_error.html.haml
index f3c8221a9d98c970d0415cd2250008c8a08c9682..3e70e98a24c55456052dc119d2235f8927e50c05 100644
--- a/app/views/errors/omniauth_error.html.haml
+++ b/app/views/errors/omniauth_error.html.haml
@@ -1,3 +1,4 @@
+- page_title "Auth Error"
 %h1 422
 %h3 Sign-in using #{@provider} auth failed
 %hr
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 2ea6cb186558187b19d27566ee9143ebe291b419..c05d45e010005bda48febd63f92e97d63f6b5873 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Groups"
 .clearfix
   .pull-left
     = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index 5086b58cd03e93e359fb991457a4e0204df4455d..ba2276f51ce722da1353750696b49b36e28802b1 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Projects"
 .clearfix
   = render 'filter'
 
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index 420f069375660faba0c25edee18f7de62ec54224..b5d146b1f2fe296769b52f6b4a5576f3b56d91ce 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -1,3 +1,4 @@
+- page_title "Starred Projects"
 .explore-trending-block
   %p.lead
     %i.fa.fa-star
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index 18749ac00ae661ab21b700d603dfaf211b4065b5..5ae2653fede07cae8a0bc6f05d772a8d993a6fe6 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -1,3 +1,4 @@
+- page_title "Trending Projects"
 .explore-title
   %h3
     Explore GitLab
diff --git a/app/views/groups/_settings_nav.html.haml b/app/views/groups/_settings_nav.html.haml
deleted file mode 100644
index f93caf90076666c3bec29a031dce33145115baf6..0000000000000000000000000000000000000000
--- a/app/views/groups/_settings_nav.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-%ul.sidebar-subnav
-  = nav_link(path: 'groups#edit') do
-    = link_to edit_group_path(@group), title: 'Group', data: {placement: 'right'} do
-      = icon('pencil-square-o')
-      %span
-        Group
-  = nav_link(path: 'groups#projects') do
-    = link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do
-      = icon('folder')
-      %span
-        Projects
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 49e7180bf9873a6d171a77481f04bfc5379f56a9..85179d4c4a2b97e706ebe610b8dae847c93db298 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Settings"
 .panel.panel-default
   .panel-heading
     %strong= @group.name
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index c0c9cd170ad6d717747fc8985bd2f27f7e08cc8a..903ca877218cf66a410a992cf3d382563f2c7255 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Members"
 - show_roles = should_user_see_group_roles?(current_user, @group)
 
 %h3.page-title
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index cf0da2da46696b37a2f34f9c0750c94a69bb5d15..6a3da6adacf47bd0299dd6bc406bbff3b66fbcbd 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,3 +1,4 @@
+- page_title "Issues"
 = content_for :meta_tags do
   - if current_user
     = auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 1ad74905636f7f3b0637696bcaa6262b36ab4766..268f33d57618a179b3cc9172a4a6ec97f5505b99 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -1,3 +1,4 @@
+- page_title "Merge Requests"
 %h3.page-title
   Merge Requests
 
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 008d5a6bd22486a89388d9e5f756255b50428297..385222fa5b7784a95152304e2dd7a9e77c4bdbe3 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Milestones"
 %h3.page-title
   Milestones
   %span.pull-right #{@group_milestones.count} milestones
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index fb32f2caa4c8bcb7d5c4523257327280884c7d31..6c41cd6b9e4316c43a6122e0d246404288a3891b 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @group_milestone.title, "Milestones"
 %h4.page-title
   .issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" }
     - if @group_milestone.closed?
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 6e17cdaef6f9c9a1870b74c592e2e359b976c607..edb882bea19a24deef769ffee0d59a48f67e6ef2 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -1,3 +1,5 @@
+- page_title    'New Group'
+- header_title  'New Group'
 = form_for @group, html: { class: 'group-form form-horizontal' } do |f|
   - if @group.errors.any?
     .alert.alert-danger
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index 0d547984cc9403f459493374020e6660b263849b..6b7efa83dead48eb166f9d4943060dd02d0b96fd 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -1,3 +1,4 @@
+- page_title "Projects"
 .panel.panel-default
   .panel-heading
     %strong= @group.name
diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml
index cc1be6a717aeaf68026f078c2d81629702a9678d..8551496b98a3107b7c6e9035ce724a9812ae142a 100644
--- a/app/views/help/show.html.haml
+++ b/app/views/help/show.html.haml
@@ -1,2 +1,3 @@
+- page_title @file.humanize, *@category.split("/").reverse.map(&:humanize)
 .documentation.wiki
   = markdown @markdown.gsub('$your_email', current_user.email)
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 246a6c1bdfd5dbd46bd4d7f915e9ec9e8a9b1317..7c89457ace3885129eaea47487e560ead6dec74a 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -1,3 +1,4 @@
+- page_title "UI Development Kit", "Help"
 - lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare."
 
 .gitlab-ui-dev-kit
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 4e49bbbc7fa1184bb16fd77049776d82eab15893..9d2858e4e727a027a3b776b117c574411864949a 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -1,3 +1,4 @@
+- page_title "Bitbucket import"
 %h3.page-title
   %i.fa.fa-bitbucket
   Import projects from Bitbucket
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index f0bc3e6b1ac91abca4c2510d93712920526381fd..ef5524982390fe4817753d043bb934157cd39601 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -1,3 +1,4 @@
+- page_title "GitHub import"
 %h3.page-title
   %i.fa.fa-github
   Import projects from GitHub
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index 33b0a21acf3b187e1c1d996c7fd3f57a6d5463f5..727f3c7e7fa69b1ace9ff715880c72d012d7f4d4 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -1,3 +1,4 @@
+- page_title "GitLab.com import"
 %h3.page-title
   %i.fa.fa-heart
   Import projects from GitLab.com
diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml
index 78c5e957be0f3d1feb65952da4de5c3a9ad8c487..bff7ee7c85d657e532cb754f5a9cb4de1953fd52 100644
--- a/app/views/import/gitorious/status.html.haml
+++ b/app/views/import/gitorious/status.html.haml
@@ -1,3 +1,4 @@
+- page_title "Gitorious import"
 %h3.page-title
   %i.icon-gitorious.icon-gitorious-big
   Import projects from Gitorious.org
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
index ce78fec205f1683207e0a21b2e7058a5266d89d2..9c64e0a009fb73931f3845306911929d33b5ef6e 100644
--- a/app/views/import/google_code/new.html.haml
+++ b/app/views/import/google_code/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Google Code import"
 %h3.page-title
   %i.fa.fa-google
   Import projects from Google Code
diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml
index 9c6824ecad7e49de7ff039f5093eacbb12041e3b..e53ebda7dc19683ab7d601aa73a4735d85504a20 100644
--- a/app/views/import/google_code/new_user_map.html.haml
+++ b/app/views/import/google_code/new_user_map.html.haml
@@ -1,3 +1,4 @@
+- page_title "User map", "Google Code import"
 %h3.page-title
   %i.fa.fa-google
   Import projects from Google Code
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index 2013b8c03c6e59489e27b6ee11650c89dbcc6b16..e8ec79e72f7fed286f5c4bfa171f48cfa418a02a 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -1,16 +1,21 @@
+- page_title "Google Code import"
 %h3.page-title
   %i.fa.fa-google
   Import projects from Google Code
 
-%p.light
-  Select projects you want to import.
-%p.light
-  Optionally, you can
-  = link_to "customize", new_user_map_import_google_code_path
-  how Google Code email addresses and usernames are imported into GitLab.
-%hr
-%p
-  = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+- if @repos.any?
+  %p.light
+    Select projects you want to import.
+  %p.light
+    Optionally, you can
+    = link_to "customize", new_user_map_import_google_code_path
+    how Google Code email addresses and usernames are imported into GitLab.
+  %hr
+  %p
+  - if @incompatible_repos.any?
+    = button_tag 'Import all compatible projects', class: "btn btn-success js-import-all"
+  - else
+    = button_tag 'Import all projects', class: "btn btn-success js-import-all"
 
 %table.table.import-jobs
   %thead
@@ -44,6 +49,22 @@
           = "#{current_user.username}/#{repo.name}"
         %td.import-actions.job-status
           = button_tag "Import", class: "btn js-add-to-import"
+    - @incompatible_repos.each do |repo|
+      %tr{id: "repo_#{repo.id}"}
+        %td
+          = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank"
+        %td.import-target
+        %td.import-actions-job-status
+          = label_tag "Incompatible Project", nil, class: "label label-danger"
+
+- if @incompatible_repos.any?
+  %p
+    One or more of your Google Code projects cannot be imported into GitLab
+    directly because they use Subversion or Mercurial for version control,
+    rather than Git. Please convert them to Git on Google Code, and go
+    through the
+    = link_to "import flow", new_import_google_code_path
+    again.
 
 :coffeescript
   new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}")
diff --git a/app/views/invites/show.html.haml b/app/views/invites/show.html.haml
index ab0ecffe4d2b24524efc69007cfaa9a5324b7d3a..2fd4859c1c628e50158caa477d424f21c1b993df 100644
--- a/app/views/invites/show.html.haml
+++ b/app/views/invites/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Invitation"
 %h3.page-title Invitation
 
 %p
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index f72882d883e77a313b88d132afad04f8209ce9bd..b1a57d9824e21dba3a3129aa142a200a1d4d6ca4 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -1,11 +1,10 @@
+- page_title "GitLab"
 %head
   %meta{charset: "utf-8"}
   %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'}
   %meta{content: "GitLab Community Edition", name: "description"}
 
-  %title
-    = "#{title} | " if defined?(title)
-    GitLab
+  %title= page_title
 
   = favicon_link_tag 'favicon.ico'
   = stylesheet_link_tag    "application", :media => "all"
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 0fa2ec9824dc44f2be60197f0d1d008d4810a217..5c55bdb546500fa4239a4a3ae5f9b0af7ce3aded 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,8 +1,8 @@
 .page-with-sidebar{ class: nav_sidebar_class }
   = render "layouts/broadcast"
   .sidebar-wrapper
-    - if defined?(sidebar)
-      = render(sidebar)
+    - if defined?(sidebar) && sidebar
+      = render "layouts/nav/#{sidebar}"
     - elsif current_user
       = render 'layouts/nav/dashboard'
     .collapse-nav
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index ab84e87c300209e2a7d3a42c7c15e9d003d86f6c..1c738719bd84b3782c7e0cd60aa88a4c2cea6cbf 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,6 +1,5 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: "Admin area"
-  %body{class: "#{app_theme}  admin", :'data-page' => body_data_page}
-    = render "layouts/head_panel", title: link_to("Admin area", admin_root_path)
-    = render 'layouts/page', sidebar: 'layouts/nav/admin'
+- page_title    "Admin area"
+- header_title  "Admin area", admin_root_path
+- sidebar       "admin"
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 6bd8ac4adb85e4e9b9e6d8469963c36b0dd20fb0..a97feeb1ecdc9caf4afc63eb2321d5eaadfd3c8b 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,6 +1,10 @@
 !!! 5
 %html{ lang: "en"}
-  = render "layouts/head", title: "Dashboard"
-  %body{class: "#{app_theme}  application", :'data-page' => body_data_page }
-    = render "layouts/head_panel", title: link_to("Dashboard", root_path)
-    = render 'layouts/page', sidebar: 'layouts/nav/dashboard'
+  = render "layouts/head"
+  %body{class: "#{app_theme}", :'data-page' => body_data_page}
+    - if current_user
+      = render "layouts/head_panel", title: header_title
+    - else
+      = render "layouts/public_head_panel", title: header_title
+
+    = render 'layouts/page', sidebar: sidebar
diff --git a/app/views/layouts/dashboard.html.haml b/app/views/layouts/dashboard.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..c72eca10bf4c379635b91a52a85a48a2e0a8a4b7
--- /dev/null
+++ b/app/views/layouts/dashboard.html.haml
@@ -0,0 +1,5 @@
+- page_title    "Dashboard"
+- header_title  "Dashboard", root_path
+- sidebar       "dashboard"
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml
index e51fd4cb8208e4e758d1a3776b55bdd97f922f95..aa0f3f0a8194b6c54d72685b994535adb77da745 100644
--- a/app/views/layouts/errors.html.haml
+++ b/app/views/layouts/errors.html.haml
@@ -1,6 +1,6 @@
 !!! 5
 %html{ lang: "en"}
-  = render "layouts/head", title: "Error"
+  = render "layouts/head"
   %body{class: "#{app_theme}  application"}
     = render "layouts/head_panel", title: "" if current_user
     .container.navless-container
diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml
index ca79382324ecaf47f7f3f354a839e72647bfa25e..56bb92a536edbb41d2b4d2fc295a14d92d3f5d3b 100644
--- a/app/views/layouts/explore.html.haml
+++ b/app/views/layouts/explore.html.haml
@@ -1,12 +1,5 @@
-- page_title = 'Explore GitLab'
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: page_title
-  %body{class: "#{app_theme}  application", :'data-page' => body_data_page}
-    = render "layouts/broadcast"
-    - if current_user
-      = render "layouts/head_panel", title: link_to(page_title, explore_root_path)
-    - else
-      = render "layouts/public_head_panel", title: link_to(page_title, explore_root_path)
+- page_title    "Explore"
+- header_title  "Explore GitLab", explore_root_path
+- sidebar       "explore"
 
-    = render 'layouts/page', sidebar: 'layouts/nav/explore'
+= render template: "layouts/application"
diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml
index f4a6bee15f68c6e6022179bb4378e7bca15a64af..5edc03129d233d0c77bdea85f2b477ec607c1233 100644
--- a/app/views/layouts/group.html.haml
+++ b/app/views/layouts/group.html.haml
@@ -1,6 +1,5 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: group_head_title
-  %body{class: "#{app_theme}  application", :'data-page' => body_data_page}
-    = render "layouts/head_panel", title: link_to(@group.name, group_path(@group))
-    = render 'layouts/page', sidebar: 'layouts/nav/group'
+- page_title    @group.name
+- header_title  @group.name, group_path(@group)
+- sidebar       "group"
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/help.html.haml b/app/views/layouts/help.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..224b24befbe8724f7b0af25a4e752c08ad654467
--- /dev/null
+++ b/app/views/layouts/help.html.haml
@@ -0,0 +1,4 @@
+- page_title    "Help"
+- header_title  "Help", help_path
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 74a8526dbd7f10a62c7729755fb94a618936f737..62f0579d48b9fad2f2b7b06d3b192eca6f5ce460 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -39,4 +39,15 @@
           = icon ('angle-down fw')
 
   - if group_settings_page?
-    = render 'groups/settings_nav'
+    %ul.sidebar-subnav
+      = nav_link(path: 'groups#edit') do
+        = link_to edit_group_path(@group), title: 'Group', data: {placement: 'right'} do
+          = icon('pencil-square-o')
+          %span
+            Group
+      = nav_link(path: 'groups#projects') do
+        = link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do
+          = icon('folder')
+          %span
+            Projects
+
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 01b3d70194fcb8225dfc969bf1c718099ba549a5..172f5197b24b0d5a74dc2958689e73b4926c648c 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,97 +1,85 @@
 %ul.project-navigation.nav.nav-sidebar
-  - if @project_settings_nav
-    = nav_link do
-      = link_to project_path(@project), title: 'Back to project', data: {placement: 'right'} do
-        = icon('caret-square-o-left fw')
+  = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
+    = link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
+      = icon('dashboard fw')
+      %span
+        Project
+  - if project_nav_tab? :files
+    = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
+      = link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files',  class: 'shortcuts-tree', data: {placement: 'right'} do
+        = icon('files-o fw')
         %span
-          Back to project
+          Files
 
-    %li.separate-item
-
-    = render 'projects/settings_nav'
-
-  - else
-    = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
-      = link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do
-        = icon('dashboard fw')
+  - if project_nav_tab? :commits
+    = nav_link(controller: %w(commit commits compare repositories tags branches)) do
+      = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
+        = icon('history fw')
         %span
-          Project
-    - if project_nav_tab? :files
-      = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
-        = link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files',  class: 'shortcuts-tree', data: {placement: 'right'} do
-          = icon('files-o fw')
-          %span
-            Files
-
-    - if project_nav_tab? :commits
-      = nav_link(controller: %w(commit commits compare repositories tags branches)) do
-        = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
-          = icon('history fw')
-          %span
-            Commits
+          Commits
 
-    - if project_nav_tab? :network
-      = nav_link(controller: %w(network)) do
-        = link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
-          = icon('code-fork fw')
-          %span
-            Network
+  - if project_nav_tab? :network
+    = nav_link(controller: %w(network)) do
+      = link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
+        = icon('code-fork fw')
+        %span
+          Network
 
-    - if project_nav_tab? :graphs
-      = nav_link(controller: %w(graphs)) do
-        = link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs',  class: 'shortcuts-graphs', data: {placement: 'right'} do
-          = icon('area-chart fw')
-          %span
-            Graphs
+  - if project_nav_tab? :graphs
+    = nav_link(controller: %w(graphs)) do
+      = link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs',  class: 'shortcuts-graphs', data: {placement: 'right'} do
+        = icon('area-chart fw')
+        %span
+          Graphs
 
-    - if project_nav_tab? :milestones
-      = nav_link(controller: :milestones) do
-        = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do
-          = icon('clock-o fw')
-          %span
-            Milestones
+  - if project_nav_tab? :milestones
+    = nav_link(controller: :milestones) do
+      = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do
+        = icon('clock-o fw')
+        %span
+          Milestones
 
-    - if project_nav_tab? :issues
-      = nav_link(controller: :issues) do
-        = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
-          = icon('exclamation-circle fw')
-          %span
-            Issues
-            - if @project.default_issues_tracker?
-              %span.count.issue_counter= @project.issues.opened.count
+  - if project_nav_tab? :issues
+    = nav_link(controller: :issues) do
+      = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do
+        = icon('exclamation-circle fw')
+        %span
+          Issues
+          - if @project.default_issues_tracker?
+            %span.count.issue_counter= @project.issues.opened.count
 
-    - if project_nav_tab? :merge_requests
-      = nav_link(controller: :merge_requests) do
-        = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
-          = icon('tasks fw')
-          %span
-            Merge Requests
-            %span.count.merge_counter= @project.merge_requests.opened.count
+  - if project_nav_tab? :merge_requests
+    = nav_link(controller: :merge_requests) do
+      = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do
+        = icon('tasks fw')
+        %span
+          Merge Requests
+          %span.count.merge_counter= @project.merge_requests.opened.count
 
-    - if project_nav_tab? :labels
-      = nav_link(controller: :labels) do
-        = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
-          = icon('tags fw')
-          %span
-            Labels
+  - if project_nav_tab? :labels
+    = nav_link(controller: :labels) do
+      = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do
+        = icon('tags fw')
+        %span
+          Labels
 
-    - if project_nav_tab? :wiki
-      = nav_link(controller: :wikis) do
-        = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do
-          = icon('book fw')
-          %span
-            Wiki
+  - if project_nav_tab? :wiki
+    = nav_link(controller: :wikis) do
+      = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do
+        = icon('book fw')
+        %span
+          Wiki
 
-    - if project_nav_tab? :snippets
-      = nav_link(controller: :snippets) do
-        = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
-          = icon('file-text-o fw')
-          %span
-            Snippets
+  - if project_nav_tab? :snippets
+    = nav_link(controller: :snippets) do
+      = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do
+        = icon('file-text-o fw')
+        %span
+          Snippets
 
-    - if project_nav_tab? :settings
-      = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
-        = link_to edit_project_path(@project), title: 'Settings', class: 'stat-tab tab no-highlight', data: {placement: 'right'} do
-          = icon('cogs fw')
-          %span
-            Settings
+  - if project_nav_tab? :settings
+    = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
+      = link_to edit_project_path(@project), title: 'Settings', class: 'stat-tab tab no-highlight', data: {placement: 'right'} do
+        = icon('cogs fw')
+        %span
+          Settings
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..21260302a09a2c2cf16c24322bf76f0d34b7ed73
--- /dev/null
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -0,0 +1,41 @@
+%ul.project-navigation.nav.nav-sidebar
+  = nav_link do
+    = link_to project_path(@project), title: 'Back to project', data: {placement: 'right'} do
+      = icon('caret-square-o-left fw')
+      %span
+        Back to project
+
+  %li.separate-item
+
+  %ul.project-settings-nav.sidebar-subnav
+    = nav_link(path: 'projects#edit') do
+      = link_to edit_project_path(@project), title: 'Project', class: 'stat-tab tab', data: {placement: 'right'} do
+        = icon('pencil-square-o')
+        %span
+          Project
+    = nav_link(controller: [:project_members, :teams]) do
+      = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
+        = icon('users')
+        %span
+          Members
+    = nav_link(controller: :deploy_keys) do
+      = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do
+        = icon('key')
+        %span
+          Deploy Keys
+    = nav_link(controller: :hooks) do
+      = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do
+        = icon('link')
+        %span
+          Web Hooks
+    = nav_link(controller: :services) do
+      = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do
+        = icon('cogs')
+        %span
+          Services
+    = nav_link(controller: :protected_branches) do
+      = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do
+        = icon('lock')
+        %span
+          Protected branches
+
diff --git a/app/views/layouts/nav/_snippets.html.haml b/app/views/layouts/nav/_snippets.html.haml
index 0de3a9e5bb77ac87b798cbd293c5c88fbf33a27b..458b76a2c99808eb02cc42a8c4ad4ca2c3de35ab 100644
--- a/app/views/layouts/nav/_snippets.html.haml
+++ b/app/views/layouts/nav/_snippets.html.haml
@@ -1,9 +1,10 @@
 %ul.nav.nav-sidebar
-  = nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do
-    = link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
-      = icon('dashboard fw')
-      %span
-        Your Snippets
+  - if current_user
+    = nav_link(path: user_snippets_path(current_user), html_options: {class: 'home'}) do
+      = link_to user_snippets_path(current_user), title: 'Your snippets', data: {placement: 'right'} do
+        = icon('dashboard fw')
+        %span
+          Your Snippets
   = nav_link(path: snippets_path) do
     = link_to snippets_path, title: 'Discover snippets', data: {placement: 'right'} do
       = icon('globe fw')
diff --git a/app/views/layouts/navless.html.haml b/app/views/layouts/navless.html.haml
deleted file mode 100644
index 10a0fcea2f80751d75041770a0d0758225c72c34..0000000000000000000000000000000000000000
--- a/app/views/layouts/navless.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: @title
-  %body{class: "#{app_theme}  application", :'data-page' => body_data_page}
-    = render "layouts/broadcast"
-    = render "layouts/head_panel", title: defined?(@title_url) ? link_to(@title, @title_url) : @title
-    = render 'layouts/page'
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 2b5be7fc37202a5ea3735941d94a44a6f96c055d..9799b4cc4d72c4f31d08dcf51ea499664097e3fd 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -1,6 +1,5 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: "Profile"
-  %body{class: "#{app_theme}  profile", :'data-page' => body_data_page}
-    = render "layouts/head_panel", title: link_to("Profile", profile_path)
-    = render 'layouts/page', sidebar: 'layouts/nav/profile'
+- page_title    "Profile"
+- header_title  "Profile", profile_path
+- sidebar       "profile"
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..4aeb9d397d2aa73a504077a6e3d2dc21eb241660
--- /dev/null
+++ b/app/views/layouts/project.html.haml
@@ -0,0 +1,8 @@
+- page_title    @project.name_with_namespace
+- header_title  project_title(@project)
+- sidebar       "project" unless sidebar
+
+- content_for :embedded_scripts do
+  = render "layouts/init_auto_complete" if current_user
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index 0a0039dec169e5f5d5d047685ebf29e1d7fbc8a3..4340166833487461454281ad4759f490380165c1 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -1,8 +1,4 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: @project.name_with_namespace
-  %body{class: "#{app_theme}  project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
-    = render "layouts/head_panel", title: project_title(@project)
-    = render "layouts/init_auto_complete"
-    - @project_settings_nav = true
-    = render 'layouts/page', sidebar: 'layouts/nav/project'
+- page_title  "Settings"
+- sidebar     "project_settings"
+
+= render template: "layouts/project"
diff --git a/app/views/layouts/projects.html.haml b/app/views/layouts/projects.html.haml
deleted file mode 100644
index dde0964f47f4961035bcf2467b24438ee0602404..0000000000000000000000000000000000000000
--- a/app/views/layouts/projects.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: project_head_title
-  %body{class: "#{app_theme}  project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
-    = render "layouts/head_panel", title: project_title(@project)
-    = render "layouts/init_auto_complete"
-    = render 'layouts/page', sidebar: 'layouts/nav/project'
diff --git a/app/views/layouts/public_group.html.haml b/app/views/layouts/public_group.html.haml
deleted file mode 100644
index b9b1d03e08ee8a05288b207fcfbad755778f6da4..0000000000000000000000000000000000000000
--- a/app/views/layouts/public_group.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: group_head_title
-  %body{class: "#{app_theme}  application", :'data-page' => body_data_page}
-    = render "layouts/public_head_panel", title: link_to(@group.name, group_path(@group))
-    = render 'layouts/page', sidebar: 'layouts/nav/group'
diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml
deleted file mode 100644
index 04fa7c84e73c75ccaf700be683062e90fa0ce75e..0000000000000000000000000000000000000000
--- a/app/views/layouts/public_projects.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: @project.name_with_namespace
-  %body{class: "#{app_theme}  application", :'data-page' => body_data_page}
-    = render "layouts/public_head_panel", title: project_title(@project)
-    = render 'layouts/page', sidebar: 'layouts/nav/project'
diff --git a/app/views/layouts/public_users.html.haml b/app/views/layouts/public_users.html.haml
deleted file mode 100644
index 71c16bd168418d1a0771abe37c692c26f12178c5..0000000000000000000000000000000000000000
--- a/app/views/layouts/public_users.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: @title
-  %body{class: "#{app_theme}  application", :'data-page' => body_data_page}
-    = render "layouts/public_head_panel", title: defined?(@title_url) ? link_to(@title, @title_url) : @title
-    = render 'layouts/page'
diff --git a/app/views/layouts/search.html.haml b/app/views/layouts/search.html.haml
index 4b526686be4058a2849798823a76ccfcbb350ada..fd4c7ad21a71501bc03cf01dffb42e56ce38305d 100644
--- a/app/views/layouts/search.html.haml
+++ b/app/views/layouts/search.html.haml
@@ -1,6 +1,4 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: "Search"
-  %body{class: "#{app_theme}  application", :'data-page' => body_data_page}
-    = render "layouts/head_panel", title: link_to("Search", search_path)
-    = render 'layouts/page'
+- page_title    "Search"
+- header_title  "Search", search_path
+
+= render template: "layouts/application"
diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml
index fbd29eb23a75d5e52f7aa3f0f19b0c9f3534395e..9b0f40073ab79061cc7da12c3ff391377ffc6fa0 100644
--- a/app/views/layouts/snippets.html.haml
+++ b/app/views/layouts/snippets.html.haml
@@ -1,6 +1,5 @@
-!!! 5
-%html{ lang: "en"}
-  = render "layouts/head", title: "Dashboard"
-  %body{class: "#{app_theme}  application", :'data-page' => body_data_page }
-    = render "layouts/head_panel", title: link_to("Snippets", snippets_path)
-    = render 'layouts/page', sidebar: 'layouts/nav/snippets'
+- page_title    'Snippets'
+- header_title  'Snippets', snippets_path
+- sidebar       "snippets"
+
+= render template: "layouts/application"
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 5bffb4acc1d7530c08a00d699be68843c1680684..1c3a3d68aca912979630929f70584b5e2c52dc09 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Account"
 - if current_user.ldap_user?
   .alert.alert-info
     Some options are unavailable for LDAP accounts
diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml
index 97e98948f36647b7021e32cd3bd7e769365f75c0..c4f6f59624bc5a97f7b71346e613bbd3ea1b73b4 100644
--- a/app/views/profiles/applications.html.haml
+++ b/app/views/profiles/applications.html.haml
@@ -1,3 +1,4 @@
+- page_title "Applications"
 %h3.page-title
   Application Settings
 %p.light
diff --git a/app/views/profiles/design.html.haml b/app/views/profiles/design.html.haml
index 646576c3164ecd7db6fd3f4fe08d4f6d1540a486..af284f6040964a59b2b3153916a7159733d1cd36 100644
--- a/app/views/profiles/design.html.haml
+++ b/app/views/profiles/design.html.haml
@@ -1,3 +1,4 @@
+- page_title "Design"
 %h3.page-title
   Design Settings
 %p.light
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index 09f290429eacb2f97599daf3ea46e26df180fd15..c17e01425d8006e0721d9594f5f2ea58b60d74bf 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Emails"
 %h3.page-title
   Email Settings
 %p.light
diff --git a/app/views/profiles/history.html.haml b/app/views/profiles/history.html.haml
index b1ab433f48fb90552dd70f288b7a62fd86c34e50..b414fb69f4ed3126d4800244283a8ee30bc1be3c 100644
--- a/app/views/profiles/history.html.haml
+++ b/app/views/profiles/history.html.haml
@@ -1,3 +1,4 @@
+- page_title "History"
 %h3.page-title
   Your Account History
 %p.light
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index 0904c50c88b99e8fffa697fa317c30786cc2b474..e3af0d4e189fe9ddc7842d776bca031ce41faacb 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "SSH Keys"
 %h3.page-title
   SSH Keys Settings
   .pull-right
diff --git a/app/views/profiles/keys/new.html.haml b/app/views/profiles/keys/new.html.haml
index ccec716d0c66f02c9c6d86e6802251ea668a5740..2bf207a322161b7f2e2e5864a1083aee1e5a28ef 100644
--- a/app/views/profiles/keys/new.html.haml
+++ b/app/views/profiles/keys/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Add SSH Keys"
 %h3.page-title Add an SSH Key
 %p.light
   Paste your public key here. Read more about how to generate a key on #{link_to "the SSH help page", help_page_path("ssh", "README")}.
diff --git a/app/views/profiles/keys/show.html.haml b/app/views/profiles/keys/show.html.haml
index cfd53298962f50c7b1fd064a3421568ad8084e3d..89f6f01581aa106d15dc61d083fdb527e7b6cb47 100644
--- a/app/views/profiles/keys/show.html.haml
+++ b/app/views/profiles/keys/show.html.haml
@@ -1 +1,2 @@
+- page_title @key.title, "SSH Keys"
 = render "key_details"
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 273e72f8a4dfc26bf133cbc98afeee4c4107b6a0..a74d97dac3bb08bbf21db2505ed07e19724634c8 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Notifications"
 %h3.page-title
   Notifications Settings
 %p.light
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 4b04b113e89bd35066320a600811ad8a679210c3..21dabbdfe2cfc4e5318cf46f6bfa52c0a6552f13 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Password"
 %h3.page-title Password Settings
 %p.light
   - if @user.password_automatically_set?
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index 8bed6e0dbee1f4fcd1e21fd1c3a654a5402324e5..9c6204963e00fd81903fb8603cc55097cda3e012 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -1,3 +1,5 @@
+- page_title    "New Password"
+- header_title  "New Password"
 %h3.page-title Setup new password
 %hr
 = form_for @user, url: profile_password_path, method: :post, html: { class: 'form-horizontal '} do |f|
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 6c745e69e4061fe83bd5a5393dc7bfc2ee57b93e..29c30905117df0b7e1ef163265b06af04df8b1fb 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Settings"
 %h3.page-title
   Profile Settings
 %p.light
diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml
deleted file mode 100644
index f8b74809b76bdae7c42949fc4b4957ad5ebf41ab..0000000000000000000000000000000000000000
--- a/app/views/projects/_settings_nav.html.haml
+++ /dev/null
@@ -1,31 +0,0 @@
-%ul.project-settings-nav.sidebar-subnav
-  = nav_link(path: 'projects#edit') do
-    = link_to edit_project_path(@project), title: 'Project', class: 'stat-tab tab', data: {placement: 'right'} do
-      = icon('pencil-square-o')
-      %span
-        Project
-  = nav_link(controller: [:project_members, :teams]) do
-    = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
-      = icon('users')
-      %span
-        Members
-  = nav_link(controller: :deploy_keys) do
-    = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do
-      = icon('key')
-      %span
-        Deploy Keys
-  = nav_link(controller: :hooks) do
-    = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do
-      = icon('link')
-      %span
-        Web Hooks
-  = nav_link(controller: :services) do
-    = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do
-      = icon('cogs')
-      %span
-        Services
-  = nav_link(controller: :protected_branches) do
-    = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do
-      = icon('lock')
-      %span
-        Protected branches
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index 89dd68d647185a0da034e66b57b89f5d389a0168..462f5b7afb09a8d608cbad67f86fbef8a6625118 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Blame", @blob.path, @ref
 %h3.page-title Blame view
 
 #tree-holder.tree-holder
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 1f61a0b940c84b940e1517111b65e67180c8d889..e78181f880128de7c67700df7efd2674c0c75970 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @blob.path, @ref
 .file-editor
   %ul.nav.nav-tabs.js-edit-mode
     %li.active
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index d78a01f6422ceed88b7dbdd057a297499adb1ff4..9b1d03b820ebba208f44024afb510ddbbbd347e0 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New File", @ref
 %h3.page-title New file
 .file-editor
   = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file') do
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 69167654c39d9fac445e6c80245c154fd1baa86f..a1d464bac59fcfb2d2bf40708f5ba65e3a52e874 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @blob.path, @ref
 %div.tree-ref-holder
   = render 'shared/ref_switcher', destination: 'blob', path: @path
 
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index a313ffcf27294a07dc1e6024d5c2ac2077d4f6f6..80acc9379083037cf997b181b9e1235dc4fd9048 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Branches"
 = render "projects/commits/head"
 %h3.page-title
   Branches
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index e5fcb98c68cb00d8f561619a77cfea206579c100..cac5dc91afd59b28d9e359e871e5d380a2144224 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Branch"
 - if @error
   .alert.alert-danger
     %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index fc721067ed4d870da8deec7ea2466d67c089804e..fc91f71e8d2c1686bedec5ade14e3e84e2eb7478 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
 = render "commit_box"
 = render "projects/diffs/diffs", diffs: @diffs, project: @project
 = render "projects/notes/notes_with_form"
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index fb1012deb74a69c0d5bc1c4084a29e921af5fa78..c8531b090a6257aaa57795f3a9af0422aa25a7d5 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Commits", @ref
 = content_for :meta_tags do
   - if current_user
     = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml
index 4745bfbeaaf061e5a1f896ed2864f0ac76b2be73..d1e579a2edeb82d2847c21c1e4bdfcddc4b81ad3 100644
--- a/app/views/projects/compare/index.html.haml
+++ b/app/views/projects/compare/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Compare"
 = render "projects/commits/head"
 
 %h3.page-title
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index 214b5bd337b5cb007189d1c4434f735609ad1cb6..3670dd5c13b81e8aa36b683de3dbe1bcb41bdee9 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{params[:from]}...#{params[:to]}"
 = render "projects/commits/head"
 
 %h3.page-title
diff --git a/app/views/projects/deploy_keys/index.html.haml b/app/views/projects/deploy_keys/index.html.haml
index 472a13a85242018cecb712a20c692fd00b23125c..2e9c5dc08c8f06d307aa8a18a3e0f6343b4f4026 100644
--- a/app/views/projects/deploy_keys/index.html.haml
+++ b/app/views/projects/deploy_keys/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Deploy Keys"
 %h3.page-title
   Deploy keys allow read-only access to the repository
 
diff --git a/app/views/projects/deploy_keys/new.html.haml b/app/views/projects/deploy_keys/new.html.haml
index 186d6b58972f256ea8719302eb4f109e8a3748ca..01c810aee180b24f580f78f1833b4606590e9e1b 100644
--- a/app/views/projects/deploy_keys/new.html.haml
+++ b/app/views/projects/deploy_keys/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Deploy Key"
 %h3.page-title New Deploy key
 %hr
 
diff --git a/app/views/projects/deploy_keys/show.html.haml b/app/views/projects/deploy_keys/show.html.haml
index 405b5bcd0d3a81e8a6f7da5c10866bafd4408b6f..7d44652af725ecd99aff497894de55e9bf43a7d6 100644
--- a/app/views/projects/deploy_keys/show.html.haml
+++ b/app/views/projects/deploy_keys/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @key.title, "Deploy Keys"
 %h3.page-title
   Deploy key:
   = @key.title
diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml
index 8eb4f795971939d0d5717b5e489e83593ea41a84..3d0ab5b85d674c95ae1f45695bb4ab72681808de 100644
--- a/app/views/projects/forks/error.html.haml
+++ b/app/views/projects/forks/error.html.haml
@@ -1,3 +1,4 @@
+- page_title "Fork project"
 - if @forked_project && !@forked_project.saved?
   .alert.alert-danger.alert-block
     %h4
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 5a6c46f320885bd11a5d489780f52cc702450756..b7a2ed68e256c2675971cdd52a44c986fa48bb92 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Fork project"
 %h3.page-title Fork project
 %p.lead
   Click to fork the project to a user or group
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index 78b4c1923dd4669f09be01b43727a22ef01c8e69..254a76e108baecbf4cd96093d4bfc05515c579fa 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -1,3 +1,4 @@
+- page_title "Commit statistics"
 = render 'head'
 
 %p.lead
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index e3d5094ddc5fb6bf41774a02741bfdf80de69f14..3a8dc89f84cc3c25b8dbf90f2eeeaf33a645a416 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Contributor statistics"
 = render 'head'
 .loading-graph
   .center
diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml
index bbaddba31b9ee4bc0a2094317fa4cf8bf701e24e..808c03148f41ceb51c43949bf7cfd83c6f0fbc60 100644
--- a/app/views/projects/hooks/index.html.haml
+++ b/app/views/projects/hooks/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Web Hooks"
 %h3.page-title
   Web hooks
 
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index 934b6b8c017212afeb9664e0c5d7ea931463ba1b..f8f2e192e291f20a8960862def746e375953f2c4 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "Import repository"
 %h3.page-title
   - if @project.import_failed?
     Import failed. Retry?
diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml
index 2d1fdafed24d31497feb9ac9dede513d38c9fbdf..39fe0fc1c4f754e2225f590d0503134bda0203ab 100644
--- a/app/views/projects/imports/show.html.haml
+++ b/app/views/projects/imports/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Import in progress"
 .save-project-loader
   .center
     %h2
diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml
index b1bc3ba0eba684a146b503fa89225bda4614e840..53b6f0879c9c568b87be742dd59e869714ec285c 100644
--- a/app/views/projects/issues/edit.html.haml
+++ b/app/views/projects/issues/edit.html.haml
@@ -1 +1,2 @@
+- page_title "Edit", "#{@issue.title} (##{@issue.iid})", "Issues"
 = render "form"
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index c2522816f3b1f808dbb02338a2767b6507b9bca9..709ea1f789739e114d07a036ff9166502d865e3a 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Issues"
 = content_for :meta_tags do
   - if current_user
     = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml
index b1bc3ba0eba684a146b503fa89225bda4614e840..da6edd5c2d2fd1b4d4859efc4ed3ed0531fe0038 100644
--- a/app/views/projects/issues/new.html.haml
+++ b/app/views/projects/issues/new.html.haml
@@ -1 +1,2 @@
+- page_title "New Issue"
 = render "form"
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index bd28d8a1db29a82f6f1cde6a54d7300a21606ebc..81478dfe568093268409b54d3e5d15d8ab589526 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{@issue.title} (##{@issue.iid})", "Issues"
 .issue
   .issue-details
     %h4.page-title
diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml
index e003d1dfe7f586ffffed167502e51f428ddbb9be..645402667fd8f80613e5333fe2f63ef64a9afd3a 100644
--- a/app/views/projects/labels/edit.html.haml
+++ b/app/views/projects/labels/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @label.name, "Labels"
 %h3
   Edit label
   %span.light #{@label.name}
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 0700e72d39c8e659a4195a009552d6e0383c28df..7d19415a7f4c55122a63211f00db8beae6ebc3a8 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Labels"
 - if can? current_user, :admin_label, @project
   = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do
     New label
diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml
index 0683ed5d4fbcf6ae9f5b0cfb3e783cc7c6084654..b3ef17025c30617da25c4502150dca9969846070 100644
--- a/app/views/projects/labels/new.html.haml
+++ b/app/views/projects/labels/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Label"
 %h3 New label
 .back-link
   = link_to namespace_project_labels_path(@project.namespace, @project) do
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 45dd410dd152cbef588dcdbac51ef862cd8a3086..c2f5cdacae74d4ecc33f98c85b9b0f2a530cede1 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
 .merge-request{'data-url' => merge_request_path(@merge_request)}
   .merge-request-details
     = render "projects/merge_requests/show/mr_title"
diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml
index 839c63986ab36a155125e67a5739f0702dd8ef6e..7e5cb07f249543eda2a5b6ce49048e01841a1039 100644
--- a/app/views/projects/merge_requests/edit.html.haml
+++ b/app/views/projects/merge_requests/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
 %h3.page-title
   = "Edit merge request ##{@merge_request.iid}"
 %hr
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index d7992bdd19ea8a0412d94cebcaa28598514c83ff..ab845a7e719bf3d9a418ee555dd822fcc9def6a1 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Merge Requests"
 .append-bottom-10
   .pull-right
     = render 'shared/issuable_search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
diff --git a/app/views/projects/merge_requests/invalid.html.haml b/app/views/projects/merge_requests/invalid.html.haml
index b9c466657de4f01fe66edf0f9f530e8664dfc9db..15bd4e2fafd1cdfd188ca278cf9c00fa455cec77 100644
--- a/app/views/projects/merge_requests/invalid.html.haml
+++ b/app/views/projects/merge_requests/invalid.html.haml
@@ -1,3 +1,4 @@
+- page_title "#{@merge_request.title} (##{@merge_request.iid})", "Merge Requests"
 .merge-request
   = render "projects/merge_requests/show/mr_title"
   = render "projects/merge_requests/show/mr_box"
diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml
index 4756903d0e00465a47b0bcdd5cfa93cd0a4414c9..b038a640f67d31da7c95339229924c70f03c6b8b 100644
--- a/app/views/projects/merge_requests/new.html.haml
+++ b/app/views/projects/merge_requests/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Merge Request"
 - if @merge_request.can_be_created
   = render 'new_submit'
 - else
diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml
index b1bc3ba0eba684a146b503fa89225bda4614e840..c09815a212a35ff914f6265d4ac1df054b52cffa 100644
--- a/app/views/projects/milestones/edit.html.haml
+++ b/app/views/projects/milestones/edit.html.haml
@@ -1 +1,2 @@
+- page_title "Edit", @milestone.title, "Milestones"
 = render "form"
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index d3eab8d6d7509d19cb1bba0bda2959999fc5974c..995eecd783014f9927e8571ff1be7c0179ed09e0 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Milestones"
 .pull-right
   - if can? current_user, :admin_milestone, @project
     = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml
index b1bc3ba0eba684a146b503fa89225bda4614e840..47149dfea41e88fcf0bcedad646c79c477d0bf56 100644
--- a/app/views/projects/milestones/new.html.haml
+++ b/app/views/projects/milestones/new.html.haml
@@ -1 +1,2 @@
+- page_title "New Milestone"
 = render "form"
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 25cc00309658282dd2b31ce9c5942676f76fdd60..bba2b8764acf19a9aff0569ce9efc41ba1411743 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @milestone.title, "Milestones"
 %h4.page-title
   .issue-box{ class: issue_box_class(@milestone) }
     - if @milestone.closed?
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index c36bad1e94b6096af8c2e0b0b056aec3e330a6d2..c67a7d256a84b04bcd97ef4312c0ff81a5825fae 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Network", @ref
 = render "head"
 .project-network
   .controls
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 47c69f89a97888c6ee3dde97a3977c9e868de584..e56d8615132f256c4ddd806b06d6bea6d7c12e64 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -1,3 +1,5 @@
+- page_title    'New Project'
+- header_title  'New Project'
 .project-edit-container
   .project-edit-errors
     = render 'projects/errors'
diff --git a/app/views/projects/project_members/import.html.haml b/app/views/projects/project_members/import.html.haml
index 293754cd0c0fa180a5f80dec6fdb78738d88306a..6914543f6dad300463d906d86e7a11b3b661f4b6 100644
--- a/app/views/projects/project_members/import.html.haml
+++ b/app/views/projects/project_members/import.html.haml
@@ -1,3 +1,4 @@
+- page_title "Import members"
 %h3.page-title
   Import members from another project
 %p.light
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 36a6f6a15547d9d05737d5ff08f475a198531dae..6edb92acd4d5eb983733b23117bc5ac274c78c26 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Members"
 %h3.page-title
   Users with access to this project
 
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index a3464c0e5e183d623abb99c5372b588349c9aee3..52b3a50c1e61f41913f11fc15d0f44be6a5d28b9 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Protected branches"
 %h3.page-title Protected branches
 %p.light Keep stable branches secure and force developers to use Merge Requests
 %hr
diff --git a/app/views/projects/services/edit.html.haml b/app/views/projects/services/edit.html.haml
index bcc5832792fea10b70d29a9fb15633f06e001dd0..50ed78286d2d6cd2c466c79964d36f8ce1afc661 100644
--- a/app/views/projects/services/edit.html.haml
+++ b/app/views/projects/services/edit.html.haml
@@ -1 +1,2 @@
+- page_title @service.title, "Services"
 = render 'form'
diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml
index 0d3ccb6bb839e7306e5b9409fee7e309d11eea3e..1065def693bbbecf3a8846b054a6735f9cda7d9a 100644
--- a/app/views/projects/services/index.html.haml
+++ b/app/views/projects/services/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Services"
 %h3.page-title Project services
 %p.light Project services allow you to integrate GitLab with other applications
 
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
index 7baddebde455944aebc557848ab5f25409b2ef87..945f0084dff1da7d823a00ffe6e17e4e08d34189 100644
--- a/app/views/projects/snippets/edit.html.haml
+++ b/app/views/projects/snippets/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @snippet.title, "Snippets"
 %h3.page-title
   Edit snippet
 %hr
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index e2d8ec673a17fe3f2e6ced456fbf3d9b5da3ba22..da9401bd8c1625a3308629ddd4afbd9251dcf3fd 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Snippets"
 %h3.page-title
   Snippets
   - if can? current_user, :write_project_snippet, @project
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
index 5efe662665ee72824a0ef2e9d8904161491cfe51..e38d95c45e7e893c5f88feee5d43ec04ddadf9c8 100644
--- a/app/views/projects/snippets/new.html.haml
+++ b/app/views/projects/snippets/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Snippets"
 %h3.page-title
   New snippet
 %hr
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index d19689a1056213a16988e15c975c09e4c2e4d888..5725d804df3f7a18b57d10bb176c1a6904ed7b37 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @snippet.title, "Snippets"
 %h3.page-title
   = @snippet.title
 
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index f1bc2bc9a2b23aa8cfc5526a610b7b4c59bb0c6f..d4652a47cbae1f5ab0b2357afe362e9132c45402 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Tags"
 = render "projects/commits/head"
 
 %h3.page-title
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index 655044438d50a412085d3e9e2ee351fefebb8ccb..172fafdeeff41a87f7acd4e6ac3206c54412031d 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Tag"
 - if @error
   .alert.alert-danger
     %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index a8a580944e1d7c0fea60a60f3413141463c107e6..72916cad182995dcd1f7f9db137aae68ea99c8cf 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @path.presence || "Files", @ref
 = content_for :meta_tags do
   - if current_user
     = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 566850cb78d3487b10593cc71ae2aa8e41c3d203..3f1dce1050c1d46975f0e1a402bef6b0b7e328d8 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @page.title, "Wiki"
 = render 'nav'
 .pull-right
   = render 'main_links'
diff --git a/app/views/projects/wikis/empty.html.haml b/app/views/projects/wikis/empty.html.haml
index 48058124f97fc28154ae2f46ea4b6fdb4a258c8e..ead99412406d6fda74ea213cb9867d5463779b0d 100644
--- a/app/views/projects/wikis/empty.html.haml
+++ b/app/views/projects/wikis/empty.html.haml
@@ -1,3 +1,4 @@
+- page_title "Wiki"
 %h3.page-title Empty page
 %hr
 .error_message
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 365edb524f43b97124a5f948001958f09a306809..825f2a161c42eb52a3161346dd5d166283fe0e41 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -1,3 +1,4 @@
+- page_title "Git Access", "Wiki"
 = render 'nav'
 .row
   .col-sm-6
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index 91291f753f772e66b579fa0c97cecfcfb8a69aab..673ec2d20e5d415fea7d0f53f139fc322f74a40c 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -1,3 +1,4 @@
+- page_title "History", @page.title, "Wiki"
 = render 'nav'
 %h3.page-title
   %span.light History for
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index ee233d9086f99d02406169cf9881d7dec7cd40ba..890ff1aed738c64d044b6177591c2175d38914cb 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -1,3 +1,4 @@
+- page_title "All Pages", "Wiki"
 = render 'nav'
 %h3.page-title
   All Pages
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index a6263e93f678e515a11429df7755a25772b0af66..83cd4c66672ddb62b268db19224e201da8b638ea 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @page.title, "Wiki"
 = render 'nav'
 %h3.page-title
   = @page.title
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index e9f2711be2a8df93d7aebafabe838e2a60586e91..60f9e9ac9de07b3a20ab866f96f83d9066fe097c 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @search_term
 = render 'search/form'
 %hr
 - if @search_term
diff --git a/app/views/snippets/current_user_index.html.haml b/app/views/snippets/current_user_index.html.haml
index 6bb2237a759c5eedbbf1c7d5e423cfc046ee8508..0718f743828651fea87fe43f0b6ba40d526c4447 100644
--- a/app/views/snippets/current_user_index.html.haml
+++ b/app/views/snippets/current_user_index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Your Snippets"
 %h3.page-title
   Your Snippets
   .pull-right
diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml
index 30aa174edfb3329b039c4da52dabf4982ba9e4c9..1a380035661d4a24ca894e70734c13b8f8f7d096 100644
--- a/app/views/snippets/edit.html.haml
+++ b/app/views/snippets/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "Edit", @snippet.title, "Snippets"
 %h3.page-title
   Edit snippet
 %hr
diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml
index 108dd0cca3ef5811d4e3ea66e982789b98f7cd7c..e9bb6a908d3db508c55534dc617c3743d7afde30 100644
--- a/app/views/snippets/index.html.haml
+++ b/app/views/snippets/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Public Snippets"
 %h3.page-title
   Public snippets
 
diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml
index 77cfd9af335ab8afde5915e4ecc64a130d547e35..a74d5e792ad428866f6125b6fcd8190b86a22a4e 100644
--- a/app/views/snippets/new.html.haml
+++ b/app/views/snippets/new.html.haml
@@ -1,3 +1,4 @@
+- page_title "New Snippet"
 %h3.page-title
   New snippet
 %hr
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 5204fb9a90702aca1d752f00e5567ca60c94a7f9..70a95abde6f2be3dfd9a6f068a85949637ef61d1 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -1,3 +1,4 @@
+- page_title @snippet.title, "Snippets"
 %h3.page-title
   = @snippet.title
 
diff --git a/app/views/snippets/user_index.html.haml b/app/views/snippets/user_index.html.haml
index df524cd18b0cb46b01612a3c6976605dccc336ba..23700eb39dafc6910859ddefac764f4734edd733 100644
--- a/app/views/snippets/user_index.html.haml
+++ b/app/views/snippets/user_index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Snippets", @user.name
 %h3.page-title
   = image_tag avatar_icon(@user.email), class: "avatar s24"
   = @user.name
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 0576c6a11b9c26d753e79eb3c34aed79d61d43c2..6ed45fedfa2d431b8825e6aeb56b401a060e2954 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,3 +1,6 @@
+- page_title    @user.name
+- header_title  @user.name, user_path(@user)
+
 = content_for :meta_tags do
   = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
 
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index 9dce495106f50cd4167eb3c4e4aa941aad6f8f66..8f8c41697406fb8ea0a3071095efbc828b359b43 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -2,13 +2,8 @@
 # four configuration values can also be set straight in your models.
 Devise.setup do |config|
   # ==> Mailer Configuration
-  # Configure the e-mail address which will be shown in Devise::Mailer,
-  # note that it will be overwritten if you use your own mailer class with default "from" parameter.
-  config.mailer_sender = "GitLab <#{Gitlab.config.gitlab.email_from}>"
-
-
   # Configure the class responsible to send e-mails.
-  # config.mailer = "Devise::Mailer"
+  config.mailer = "DeviseMailer"
 
   # ==> ORM configuration
   # Load and configure the ORM. Supports :active_record (default) and
diff --git a/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb b/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4ca676f6c72908f3281ad3e64f5efb5bf8a93bb9
--- /dev/null
+++ b/db/migrate/20150425164648_add_missing_unique_indices.acts_as_taggable_on_engine.rb
@@ -0,0 +1,20 @@
+# This migration comes from acts_as_taggable_on_engine (originally 2)
+class AddMissingUniqueIndices < ActiveRecord::Migration
+  def self.up
+    add_index :tags, :name, unique: true
+
+    remove_index :taggings, :tag_id
+    remove_index :taggings, [:taggable_id, :taggable_type, :context]
+    add_index :taggings,
+              [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type],
+              unique: true, name: 'taggings_idx'
+  end
+
+  def self.down
+    remove_index :tags, :name
+
+    remove_index :taggings, name: 'taggings_idx'
+    add_index :taggings, :tag_id
+    add_index :taggings, [:taggable_id, :taggable_type, :context]
+  end
+end
diff --git a/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb b/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8edb508078131059ac506257527d5dc02bad67be
--- /dev/null
+++ b/db/migrate/20150425164649_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb
@@ -0,0 +1,15 @@
+# This migration comes from acts_as_taggable_on_engine (originally 3)
+class AddTaggingsCounterCacheToTags < ActiveRecord::Migration
+  def self.up
+    add_column :tags, :taggings_count, :integer, default: 0
+
+    ActsAsTaggableOn::Tag.reset_column_information
+    ActsAsTaggableOn::Tag.find_each do |tag|
+      ActsAsTaggableOn::Tag.reset_counters(tag.id, :taggings)
+    end
+  end
+
+  def self.down
+    remove_column :tags, :taggings_count
+  end
+end
diff --git a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
new file mode 100644
index 0000000000000000000000000000000000000000..71f2d7f43309d0628ec0120e211c945559c46846
--- /dev/null
+++ b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb
@@ -0,0 +1,10 @@
+# This migration comes from acts_as_taggable_on_engine (originally 4)
+class AddMissingTaggableIndex < ActiveRecord::Migration
+  def self.up
+    add_index :taggings, [:taggable_id, :taggable_type, :context]
+  end
+
+  def self.down
+    remove_index :taggings, [:taggable_id, :taggable_type, :context]
+  end
+end
diff --git a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bfb06bc7cda48c12580961fc53373a6153ac7f68
--- /dev/null
+++ b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
@@ -0,0 +1,10 @@
+# This migration comes from acts_as_taggable_on_engine (originally 5)
+# This migration is added to circumvent issue #623 and have special characters
+# work properly
+class ChangeCollationForTagNames < ActiveRecord::Migration
+  def up
+    if ActsAsTaggableOn::Utils.using_mysql?
+      execute("ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin;")
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index a2ed9efc93362b8f183434f49dd940d6a2fb93b8..02882186fc85814cd04fcc19672c6a2de229a7ca 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -433,13 +433,16 @@ ActiveRecord::Schema.define(version: 20150429002313) do
     t.datetime "created_at"
   end
 
-  add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
+  add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true, using: :btree
   add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
 
   create_table "tags", force: true do |t|
-    t.string "name"
+    t.string  "name"
+    t.integer "taggings_count", default: 0
   end
 
+  add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
+
   create_table "users", force: true do |t|
     t.string   "email",                         default: "",    null: false
     t.string   "encrypted_password",            default: "",    null: false
diff --git a/doc/api/README.md b/doc/api/README.md
index dec530d0b812022be099d3023f279de0b3e850dc..f6757b0a6aa30083eb5212f39547882782ba4f99 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -6,6 +6,7 @@
 - [Session](session.md)
 - [Projects](projects.md)
 - [Project Snippets](project_snippets.md)
+- [Services](services.md)
 - [Repositories](repositories.md)
 - [Repository Files](repository_files.md)
 - [Commits](commits.md)
diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md
index e32984c4e68f2fbcf1893740e505b7f3638d112a..2cca46c86a3c06ea916cd6891f8f9b487a093574 100644
--- a/doc/update/upgrader.md
+++ b/doc/update/upgrader.md
@@ -27,10 +27,10 @@ If you have local changes to your GitLab repository the script will stash them a
 Note: GitLab 7.9 adds `nodejs` as a dependency. GitLab 7.6 adds `libkrb5-dev` as a dependency (installed by default on Ubuntu and OSX). GitLab 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
 
     cd /home/git/gitlab
-    sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.0"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute'
+    sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.1"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute'
 
     # to perform a non-interactive install (no user input required) you can add -y
-    # sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.0"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y
+    # sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.1"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y
 
 ## 3. Start application
 
@@ -65,7 +65,7 @@ Here is a one line command with step 1 to 5 for the next time you upgrade:
 cd /home/git/gitlab; \
   sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production; \
   sudo service gitlab stop; \
-  sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.0"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y; \
+  sudo -u git -H ruby -Ilib -e 'require "gitlab/upgrader"' -e 'class Gitlab::Upgrader' -e 'def latest_version_raw' -e '"v7.10.1"' -e 'end' -e 'end' -e 'Gitlab::Upgrader.new.execute' -- -y; \
   cd /home/git/gitlab-shell; \
   sudo -u git -H git fetch; \
   sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION`; \
diff --git a/features/project/project.feature b/features/project/project.feature
index 3e1fd54bee84ca92fcbb4cd61880287715e6e0b4..ae28312a69ac09576defd9d0a1f0c5fde3c078fb 100644
--- a/features/project/project.feature
+++ b/features/project/project.feature
@@ -55,3 +55,10 @@ Feature: Project
     Then I should see project "Forum" README
     And I visit project "Shop" page
     Then I should see project "Shop" README
+
+  Scenario: I tag a project
+    When I visit edit project "Shop" page
+    Then I should see project settings
+    And I add project tags
+    And I save project
+    Then I should see project tags
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index d39c8e7d2db82d10935ce52e5b06aa716c080db0..f14396bcfffc7374f950e844589a68f6e01222f4 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -94,4 +94,12 @@ class Spinach::Features::Project < Spinach::FeatureSteps
     page.should have_link 'README.md'
     page.should have_content 'testme'
   end
+
+  step 'I add project tags' do
+    fill_in 'Tags', with: 'tag1, tag2'
+  end
+
+  step 'I should see project tags' do
+    expect(find_field('Tags').value).to eq 'tag1, tag2'
+  end
 end
diff --git a/features/steps/shared/markdown.rb b/features/steps/shared/markdown.rb
index e71700880cde96622a3a307ad47e79deaba25e80..a7231c47d14306829efc0653454a4fa46728d601 100644
--- a/features/steps/shared/markdown.rb
+++ b/features/steps/shared/markdown.rb
@@ -2,8 +2,12 @@ module SharedMarkdown
   include Spinach::DSL
 
   def header_should_have_correct_id_and_link(level, text, id, parent = ".wiki")
-    find(:css, "#{parent} h#{level}##{id}").text.should == text
-    find(:css, "#{parent} h#{level}##{id} > :last-child")[:href].should =~ /##{id}$/
+    node = find("#{parent} h#{level} a##{id}")
+    node[:href].should == "##{id}"
+
+    # Work around a weird Capybara behavior where calling `parent` on a node
+    # returns the whole document, not the node's actual parent element
+    find(:xpath, "#{node.path}/..").text.should == text
   end
 
   def create_taskable(type, title)
diff --git a/lib/gitlab/google_code_import/client.rb b/lib/gitlab/google_code_import/client.rb
index 02f31e45f885f8b753610eac2bec2e786286c227..890bd9a35541e1fff14c728957ec9ade908ad2a1 100644
--- a/lib/gitlab/google_code_import/client.rb
+++ b/lib/gitlab/google_code_import/client.rb
@@ -21,6 +21,10 @@ module Gitlab
         @repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.select(&:git?)
       end
 
+      def incompatible_repos
+        @incompatible_repos ||= raw_data["projects"].map { |raw_repo| GoogleCodeImport::Repository.new(raw_repo) }.reject(&:git?)
+      end
+
       def repo(id)
         repos.find { |repo| repo.id == id }
       end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index 44779d7fdd82c746c476668f2934c49a39f55f19..8348e28d0f51b92b4bc6fc5abe1a5fc27c5cb779 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -3,33 +3,10 @@ require 'html/pipeline'
 module Gitlab
   # Custom parser for GitLab-flavored Markdown
   #
-  # It replaces references in the text with links to the appropriate items in
-  # GitLab.
-  #
-  # Supported reference formats are:
-  #   * @foo for team members
-  #   * #123 for issues
-  #   * JIRA-123 for Jira issues
-  #   * !123 for merge requests
-  #   * $123 for snippets
-  #   * 1c002d for specific commit
-  #   * 1c002d...35cfb2 for commit ranges (comparisons)
-  #
-  # It also parses Emoji codes to insert images. See
-  # http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
-  #
-  # Examples
-  #
-  #   >> gfm("Hey @david, can you fix this?")
-  #   => "Hey <a href="/u/david">@david</a>, can you fix this?"
-  #
-  #   >> gfm("Commit 35d5f7c closes #1234")
-  #   => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
-  #
-  #   >> gfm(":trollface:")
-  #   => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
+  # See the files in `lib/gitlab/markdown/` for specific processing information.
   module Markdown
     # Provide autoload paths for filters to prevent a circular dependency error
+    autoload :AutolinkFilter,               'gitlab/markdown/autolink_filter'
     autoload :CommitRangeReferenceFilter,   'gitlab/markdown/commit_range_reference_filter'
     autoload :CommitReferenceFilter,        'gitlab/markdown/commit_reference_filter'
     autoload :EmojiFilter,                  'gitlab/markdown/emoji_filter'
@@ -37,7 +14,9 @@ module Gitlab
     autoload :IssueReferenceFilter,         'gitlab/markdown/issue_reference_filter'
     autoload :LabelReferenceFilter,         'gitlab/markdown/label_reference_filter'
     autoload :MergeRequestReferenceFilter,  'gitlab/markdown/merge_request_reference_filter'
+    autoload :SanitizationFilter,           'gitlab/markdown/sanitization_filter'
     autoload :SnippetReferenceFilter,       'gitlab/markdown/snippet_reference_filter'
+    autoload :TableOfContentsFilter,        'gitlab/markdown/table_of_contents_filter'
     autoload :UserReferenceFilter,          'gitlab/markdown/user_reference_filter'
 
     # Public: Parse the provided text with GitLab-Flavored Markdown
@@ -74,13 +53,13 @@ module Gitlab
       pipeline = HTML::Pipeline.new(filters)
 
       context = {
-        # SanitizationFilter
-        whitelist: sanitization_whitelist,
-
         # EmojiFilter
         asset_root: Gitlab.config.gitlab.url,
         asset_host: Gitlab::Application.config.asset_host,
 
+        # TableOfContentsFilter
+        no_header_anchors: options[:no_header_anchors],
+
         # ReferenceFilter
         current_user:    current_user,
         only_path:       options[:reference_only_path],
@@ -111,12 +90,14 @@ module Gitlab
     # SanitizationFilter should come first so that all generated reference HTML
     # goes through untouched.
     #
-    # See https://gitlab.com/gitlab-org/html-pipeline-gitlab for more filters
+    # See https://github.com/jch/html-pipeline#filters for more filters.
     def filters
       [
-        HTML::Pipeline::SanitizationFilter,
+        Gitlab::Markdown::SanitizationFilter,
 
         Gitlab::Markdown::EmojiFilter,
+        Gitlab::Markdown::TableOfContentsFilter,
+        Gitlab::Markdown::AutolinkFilter,
 
         Gitlab::Markdown::UserReferenceFilter,
         Gitlab::Markdown::IssueReferenceFilter,
@@ -125,36 +106,10 @@ module Gitlab
         Gitlab::Markdown::SnippetReferenceFilter,
         Gitlab::Markdown::CommitRangeReferenceFilter,
         Gitlab::Markdown::CommitReferenceFilter,
-        Gitlab::Markdown::LabelReferenceFilter,
+        Gitlab::Markdown::LabelReferenceFilter
       ]
     end
 
-    # Customize the SanitizationFilter whitelist
-    #
-    # - Allow `class` and `id` attributes on all elements
-    # - Allow `span` elements
-    # - Remove `rel` attributes from `a` elements
-    # - Remove `a` nodes with `javascript:` in the `href` attribute
-    def sanitization_whitelist
-      whitelist = HTML::Pipeline::SanitizationFilter::WHITELIST
-      whitelist[:attributes][:all].push('class', 'id')
-      whitelist[:elements].push('span')
-
-      fix_anchors = lambda do |env|
-        name, node = env[:node_name], env[:node]
-        if name == 'a'
-          node.remove_attribute('rel')
-          if node['href'] && node['href'].match('javascript:')
-            node.remove_attribute('href')
-          end
-        end
-      end
-
-      whitelist[:transformers].push(fix_anchors)
-
-      whitelist
-    end
-
     # Turn list items that start with "[ ]" into HTML checkbox inputs.
     def parse_tasks(text)
       li_tag = '<li class="task-list-item">'
diff --git a/lib/gitlab/markdown/autolink_filter.rb b/lib/gitlab/markdown/autolink_filter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4e14a048cfb3dc96e3cb9c4ca7024b1347499de3
--- /dev/null
+++ b/lib/gitlab/markdown/autolink_filter.rb
@@ -0,0 +1,100 @@
+require 'html/pipeline/filter'
+require 'uri'
+
+module Gitlab
+  module Markdown
+    # HTML Filter for auto-linking URLs in HTML.
+    #
+    # Based on HTML::Pipeline::AutolinkFilter
+    #
+    # Context options:
+    #   :autolink  - Boolean, skips all processing done by this filter when false
+    #   :link_attr - Hash of attributes for the generated links
+    #
+    class AutolinkFilter < HTML::Pipeline::Filter
+      include ActionView::Helpers::TagHelper
+
+      # Pattern to match text that should be autolinked.
+      #
+      # A URI scheme begins with a letter and may contain letters, numbers,
+      # plus, period and hyphen. Schemes are case-insensitive but we're being
+      # picky here and allowing only lowercase for autolinks.
+      #
+      # See http://en.wikipedia.org/wiki/URI_scheme
+      #
+      # The negative lookbehind ensures that users can paste a URL followed by a
+      # period or comma for punctuation without those characters being included
+      # in the generated link.
+      #
+      # Rubular: http://rubular.com/r/cxjPyZc7Sb
+      LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://\S+)(?<!,|\.)}
+
+      # Text matching LINK_PATTERN inside these elements will not be linked
+      IGNORE_PARENTS = %w(a code kbd pre script style).to_set
+
+      def call
+        return doc if context[:autolink] == false
+
+        rinku_parse
+        text_parse
+      end
+
+      private
+
+      # Run the text through Rinku as a first pass
+      #
+      # This will quickly autolink http(s) and ftp links.
+      #
+      # `@doc` will be re-parsed with the HTML String from Rinku.
+      def rinku_parse
+        # Convert the options from a Hash to a String that Rinku expects
+        options = tag_options(link_options)
+
+        # NOTE: We don't parse email links because it will erroneously match
+        # external Commit and CommitRange references.
+        #
+        # The final argument tells Rinku to link short URLs that don't include a
+        # period (e.g., http://localhost:3000/)
+        rinku = Rinku.auto_link(html, :urls, options, IGNORE_PARENTS.to_a, 1)
+
+        # Rinku returns a String, so parse it back to a Nokogiri::XML::Document
+        # for further processing.
+        @doc = parse_html(rinku)
+      end
+
+      # Autolinks any text matching LINK_PATTERN that Rinku didn't already
+      # replace
+      def text_parse
+        search_text_nodes(doc).each do |node|
+          content = node.to_html
+
+          next if has_ancestor?(node, IGNORE_PARENTS)
+          next unless content.match(LINK_PATTERN)
+
+          # If Rinku didn't link this, there's probably a good reason, so we'll
+          # skip it too
+          next if content.start_with?(*%w(http https ftp))
+
+          html = autolink_filter(content)
+
+          next if html == content
+
+          node.replace(html)
+        end
+
+        doc
+      end
+
+      def autolink_filter(text)
+        text.gsub(LINK_PATTERN) do |match|
+          options = link_options.merge(href: match)
+          content_tag(:a, match, options)
+        end
+      end
+
+      def link_options
+        @link_options ||= context[:link_attr] || {}
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb
index 4b360369d3768f5905c05fd41aa9b275e9706f6f..1e885615163092b9756417e544bad8af59ee8ea5 100644
--- a/lib/gitlab/markdown/issue_reference_filter.rb
+++ b/lib/gitlab/markdown/issue_reference_filter.rb
@@ -44,21 +44,20 @@ module Gitlab
       # Returns a String with `#123` references replaced with links. All links
       # have `gfm` and `gfm-issue` class names attached for styling.
       def issue_link_filter(text)
-        self.class.references_in(text) do |match, issue, project_ref|
+        self.class.references_in(text) do |match, id, project_ref|
           project = self.project_from_ref(project_ref)
 
-          if project && project.issue_exists?(issue)
-            # FIXME (rspeicher): Law of Demeter
-            push_result(:issue, project.issues.where(iid: issue).first)
+          if project && issue = project.get_issue(id)
+            push_result(:issue, issue)
 
-            url = url_for_issue(issue, project, only_path: context[:only_path])
+            url = url_for_issue(id, project, only_path: context[:only_path])
 
-            title = escape_once("Issue: #{title_for_issue(issue, project)}")
+            title = escape_once("Issue: #{issue.title}")
             klass = reference_class(:issue)
 
             %(<a href="#{url}"
                  title="#{title}"
-                 class="#{klass}">#{project_ref}##{issue}</a>)
+                 class="#{klass}">#{project_ref}##{id}</a>)
           else
             match
           end
@@ -68,10 +67,6 @@ module Gitlab
       def url_for_issue(*args)
         IssuesHelper.url_for_issue(*args)
       end
-
-      def title_for_issue(*args)
-        IssuesHelper.title_for_issue(*args)
-      end
     end
   end
 end
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index 7c28fe112efcd0b3a8cb3bf9c811b7bb6e1ec50a..740d72abb3699025c6cdaf25fbb01c5764784d60 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -64,7 +64,6 @@ module Gitlab
         end
       end
 
-      # TODO (rspeicher): Cleanup
       def url_for_merge_request(mr, project)
         h = Rails.application.routes.url_helpers
         h.namespace_project_merge_request_url(project.namespace, project, mr,
diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9a154e0b2feb08447b1e5bd462e61c7d8d8d95d2
--- /dev/null
+++ b/lib/gitlab/markdown/sanitization_filter.rb
@@ -0,0 +1,38 @@
+require 'html/pipeline/filter'
+require 'html/pipeline/sanitization_filter'
+
+module Gitlab
+  module Markdown
+    # Sanitize HTML
+    #
+    # Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
+    class SanitizationFilter < HTML::Pipeline::SanitizationFilter
+      def whitelist
+        whitelist = HTML::Pipeline::SanitizationFilter::WHITELIST
+
+        # Allow `class` and `id` on all elements
+        whitelist[:attributes][:all].push('class', 'id')
+
+        # Allow table alignment
+        whitelist[:attributes]['th'] = %w(style)
+        whitelist[:attributes]['td'] = %w(style)
+
+        # Allow span elements
+        whitelist[:elements].push('span')
+
+        # Remove `rel` attribute from `a` elements
+        whitelist[:transformers].push(remove_rel)
+
+        whitelist
+      end
+
+      def remove_rel
+        lambda do |env|
+          if env[:node_name] == 'a'
+            env[:node].remove_attribute('rel')
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/markdown/table_of_contents_filter.rb b/lib/gitlab/markdown/table_of_contents_filter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c97612dafb8eca4510be2688aa27e3e271cbd60c
--- /dev/null
+++ b/lib/gitlab/markdown/table_of_contents_filter.rb
@@ -0,0 +1,62 @@
+require 'html/pipeline/filter'
+
+module Gitlab
+  module Markdown
+    # HTML filter that adds an anchor child element to all Headers in a
+    # document, so that they can be linked to.
+    #
+    # Generates the Table of Contents with links to each header. See Results.
+    #
+    # Based on HTML::Pipeline::TableOfContentsFilter.
+    #
+    # Context options:
+    #   :no_header_anchors - Skips all processing done by this filter.
+    #
+    # Results:
+    #   :toc - String containing Table of Contents data as a `ul` element with
+    #          `li` child elements.
+    class TableOfContentsFilter < HTML::Pipeline::Filter
+      PUNCTUATION_REGEXP = /[^\p{Word}\- ]/u
+
+      def call
+        return doc if context[:no_header_anchors]
+
+        result[:toc] = ""
+
+        headers = Hash.new(0)
+
+        doc.css('h1, h2, h3, h4, h5, h6').each do |node|
+          text = node.text
+
+          id = text.downcase
+          id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation
+          id.gsub!(' ', '-') # replace spaces with dash
+          id.squeeze!(' -') # replace multiple spaces or dashes with one
+
+          uniq = (headers[id] > 0) ? "-#{headers[id]}" : ''
+          headers[id] += 1
+
+          if header_content = node.children.first
+            href = "#{id}#{uniq}"
+            push_toc(href, text)
+            header_content.add_previous_sibling(anchor_tag(href))
+          end
+        end
+
+        result[:toc] = %Q{<ul class="section-nav">\n#{result[:toc]}</ul>} unless result[:toc].empty?
+
+        doc
+      end
+
+      private
+
+      def anchor_tag(href)
+        %Q{<a id="#{href}" class="anchor" href="##{href}" aria-hidden="true"></a>}
+      end
+
+      def push_toc(href, text)
+        result[:toc] << %Q{<li><a href="##{href}">#{text}</a></li>\n}
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/theme.rb b/lib/gitlab/theme.rb
index f0e61aa2e81ad6b8c94652f7b8b55c814e04fc48..e5a1f1b44d96b28edae0007406cbafa2b09931a0 100644
--- a/lib/gitlab/theme.rb
+++ b/lib/gitlab/theme.rb
@@ -40,7 +40,7 @@ module Gitlab
     end
 
     # Convenience method to get a space-separated String of all the theme
-    # classes that mighty be applied to the `body` element
+    # classes that might be applied to the `body` element
     #
     # Returns a String
     def self.body_classes
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index 10efff2ae9f12f7b250c82f2cd7907a27bd7e32b..bea66e6cdc12ee10c1eeefdc702a708e7368a5aa 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -1,5 +1,6 @@
-class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
+require 'active_support/core_ext/string/output_safety'
 
+class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
   attr_reader :template
   alias_method :h, :template
 
@@ -8,24 +9,12 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
     @color_scheme = color_scheme
     @project = @template.instance_variable_get("@project")
     @options = options.dup
-    super options
-  end
 
-  def preprocess(full_document)
-    # Redcarpet doesn't allow SMB links when `safe_links_only` is enabled.
-    # FTP links are allowed, so we trick Redcarpet.
-    full_document.gsub("smb://", "ftp://smb:")
+    super(options)
   end
 
-  # If project has issue number 39, apostrophe will be linked in
-  # regular text to the issue as Redcarpet will convert apostrophe to
-  # #39;
-  # We replace apostrophe with right single quote before Redcarpet
-  # does the processing and put the apostrophe back in postprocessing.
-  # This only influences regular text, code blocks are untouched.
   def normal_text(text)
-    return text unless text.present?
-    text.gsub("'", "&rsquo;")
+    ERB::Util.html_escape_once(text)
   end
 
   # Stolen from Rugments::Plugins::Redcarpet as this module is not required
@@ -37,7 +26,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
     # so we assume you're not using leading spaces that aren't tabs,
     # and just replace them here.
     if lexer.tag == 'make'
-      code.gsub! /^    /, "\t"
+      code.gsub!(/^    /, "\t")
     end
 
     formatter = Rugments::Formatters::HTML.new(
@@ -46,27 +35,11 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
     formatter.format(lexer.lex(code))
   end
 
-  def link(link, title, content)
-    h.link_to_gfm(content, link, title: title)
-  end
-
-  def header(text, level)
-    if @options[:no_header_anchors]
-      "<h#{level}>#{text}</h#{level}>"
-    else
-      id = ActionController::Base.helpers.strip_tags(h.gfm(text)).downcase() \
-          .gsub(/[^a-z0-9_-]/, '-').gsub(/-+/, '-').gsub(/^-/, '').gsub(/-$/, '')
-      "<h#{level} id=\"#{id}\">#{text}<a href=\"\##{id}\"></a></h#{level}>"
-    end
-  end
-
   def postprocess(full_document)
-    full_document.gsub!("ftp://smb:", "smb://")
-
-    full_document.gsub!("&rsquo;", "'")
     unless @template.instance_variable_get("@project_wiki") || @project.nil?
       full_document = h.create_relative_links(full_document)
     end
+
     h.gfm_with_options(full_document, @options)
   end
 end
diff --git a/spec/controllers/import/google_code_controller_spec.rb b/spec/controllers/import/google_code_controller_spec.rb
index 037cddb460020c34af6a489de6707f0a86f02fd6..78c0f5079cc6ee6157ff7a35486493e178dda94c 100644
--- a/spec/controllers/import/google_code_controller_spec.rb
+++ b/spec/controllers/import/google_code_controller_spec.rb
@@ -27,21 +27,34 @@ describe Import::GoogleCodeController do
     it "assigns variables" do
       @project = create(:project, import_type: 'google_code', creator_id: user.id)
       controller.stub_chain(:client, :repos).and_return([@repo])
+      controller.stub_chain(:client, :incompatible_repos).and_return([])
 
       get :status
 
       expect(assigns(:already_added_projects)).to eq([@project])
       expect(assigns(:repos)).to eq([@repo])
+      expect(assigns(:incompatible_repos)).to eq([])
     end
 
     it "does not show already added project" do
       @project = create(:project, import_type: 'google_code', creator_id: user.id, import_source: 'vim')
       controller.stub_chain(:client, :repos).and_return([@repo])
+      controller.stub_chain(:client, :incompatible_repos).and_return([])
 
       get :status
 
       expect(assigns(:already_added_projects)).to eq([@project])
       expect(assigns(:repos)).to eq([])
     end
+
+    it "does not show any invalid projects" do
+      controller.stub_chain(:client, :repos).and_return([])
+      controller.stub_chain(:client, :incompatible_repos).and_return([@repo])
+
+      get :status
+
+      expect(assigns(:repos)).to be_empty
+      expect(assigns(:incompatible_repos)).to eq([@repo])
+    end
   end
 end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 57fa079d753894f7b9f542325222f4888f1b70ce..102678a1d74f1baa768ed9cd9971e04c68193872 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -94,10 +94,26 @@ FactoryGirl.define do
           'new_issue_url' => 'http://redmine/projects/project_name_in_redmine/issues/new'
         }
       )
-    end
-    after :create do |project|
+
       project.issues_tracker = 'redmine'
       project.issues_tracker_id = 'project_name_in_redmine'
     end
   end
+
+  factory :jira_project, parent: :project do
+    after :create do |project|
+      project.create_jira_service(
+        active: true,
+        properties: {
+          'title'         => 'JIRA tracker',
+          'project_url'   => 'http://jira.example/issues/?jql=project=A',
+          'issues_url'    => 'http://jira.example/browse/:id',
+          'new_issue_url' => 'http://jira.example/secure/CreateIssue.jspa'
+        }
+      )
+
+      project.issues_tracker = 'jira'
+      project.issues_tracker_id = 'project_name_in_jira'
+    end
+  end
 end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3528200e12bdf445e7d1c01ee6eb473f954847d3
--- /dev/null
+++ b/spec/features/markdown_spec.rb
@@ -0,0 +1,391 @@
+require 'spec_helper'
+require 'erb'
+
+# This feature spec is intended to be a comprehensive exercising of all of
+# GitLab's non-standard Markdown parsing and the integration thereof.
+#
+# These tests should be very high-level. Anything low-level belongs in the specs
+# for the corresponding HTML::Pipeline filter or helper method.
+#
+# The idea is to pass a Markdown document through our entire processing stack.
+#
+# The process looks like this:
+#
+#   Raw Markdown
+#   -> `markdown` helper
+#     -> Redcarpet::Render::GitlabHTML converts Markdown to HTML
+#       -> Post-process HTML
+#         -> `gfm_with_options` helper
+#           -> HTML::Pipeline
+#             -> Sanitize
+#             -> Emoji
+#             -> Table of Contents
+#             -> Autolinks
+#               -> Rinku (http, https, ftp)
+#               -> Other schemes
+#             -> References
+#           -> `html_safe`
+#           -> Template
+#
+# See the MarkdownFeature class for setup details.
+
+describe 'GitLab Markdown' do
+  include ActionView::Helpers::TagHelper
+  include ActionView::Helpers::UrlHelper
+  include Capybara::Node::Matchers
+  include GitlabMarkdownHelper
+
+  # `markdown` calls these two methods
+  def current_user
+    @feat.user
+  end
+
+  def user_color_scheme_class
+    :white
+  end
+
+  # Let's only parse this thing once
+  before(:all) do
+    @feat = MarkdownFeature.new
+
+    # `markdown` expects a `@project` variable
+    @project = @feat.project
+
+    @md = markdown(@feat.raw_markdown)
+    @doc = Nokogiri::HTML::DocumentFragment.parse(@md)
+  end
+
+  after(:all) do
+    @feat.teardown
+  end
+
+  # Given a header ID, goes to that element's parent (the header), then to its
+  # second sibling (the body).
+  def get_section(id)
+    @doc.at_css("##{id}").parent.next.next
+  end
+
+  # it 'writes to a file' do
+  #   File.open(Rails.root.join('tmp/capybara/markdown_spec.html'), 'w') do |file|
+  #     file.puts @md
+  #   end
+  # end
+
+  describe 'Markdown' do
+    describe 'No Intra Emphasis' do
+      it 'does not parse emphasis inside of words' do
+        body = get_section('no-intra-emphasis')
+        expect(body.to_html).not_to match('foo<em>bar</em>baz')
+      end
+    end
+
+    describe 'Tables' do
+      it 'parses table Markdown' do
+        body = get_section('tables')
+        expect(body).to have_selector('th:contains("Header")')
+        expect(body).to have_selector('th:contains("Row")')
+        expect(body).to have_selector('th:contains("Example")')
+      end
+
+      it 'allows Markdown in tables' do
+        expect(@doc.at_css('td:contains("Baz")').children.to_html).
+          to eq '<strong>Baz</strong>'
+      end
+    end
+
+    describe 'Fenced Code Blocks' do
+      it 'parses fenced code blocks' do
+        expect(@doc).to have_selector('pre.code.highlight.white.c')
+        expect(@doc).to have_selector('pre.code.highlight.white.python')
+      end
+    end
+
+    describe 'Strikethrough' do
+      it 'parses strikethroughs' do
+        expect(@doc).to have_selector(%{del:contains("and this text doesn't")})
+      end
+    end
+
+    describe 'Superscript' do
+      it 'parses superscript' do
+        body = get_section('superscript')
+        expect(body.to_html).to match('1<sup>st</sup>')
+        expect(body.to_html).to match('2<sup>nd</sup>')
+      end
+    end
+  end
+
+  describe 'HTML::Pipeline' do
+    describe 'SanitizationFilter' do
+      it 'uses a permissive whitelist' do
+        expect(@doc).to have_selector('b#manual-b')
+        expect(@doc).to have_selector('em#manual-em')
+        expect(@doc).to have_selector("code#manual-code")
+        expect(@doc).to have_selector('kbd:contains("s")')
+        expect(@doc).to have_selector('strike:contains(Emoji)')
+        expect(@doc).to have_selector('img#manual-img')
+        expect(@doc).to have_selector('br#manual-br')
+        expect(@doc).to have_selector('hr#manual-hr')
+      end
+
+      it 'permits span elements' do
+        expect(@doc).to have_selector('span#span-class-light.light')
+      end
+
+      it 'permits table alignment' do
+        expect(@doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
+        expect(@doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
+        expect(@doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
+
+        expect(@doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
+        expect(@doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
+        expect(@doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
+      end
+
+      it 'removes `rel` attribute from links' do
+        expect(@doc).to have_selector('a#a-rel-nofollow')
+        expect(@doc).not_to have_selector('a#a-rel-nofollow[rel]')
+      end
+
+      it "removes `href` from `a` elements if it's fishy" do
+        expect(@doc).to have_selector('a#a-href-javascript')
+        expect(@doc).not_to have_selector('a#a-href-javascript[href]')
+      end
+    end
+
+    describe 'Escaping' do
+      let(:table) { @doc.css('table').last.at_css('tbody') }
+
+      it 'escapes non-tag angle brackets' do
+        expect(table.at_xpath('.//tr[1]/td[3]').inner_html).to eq '1 &lt; 3 &amp; 5'
+      end
+    end
+
+    describe 'EmojiFilter' do
+      it 'parses Emoji' do
+        expect(@doc).to have_selector('img.emoji', count: 10)
+      end
+    end
+
+    describe 'TableOfContentsFilter' do
+      it 'creates anchors inside header elements' do
+        expect(@doc).to have_selector('h1 a#gitlab-markdown')
+        expect(@doc).to have_selector('h2 a#markdown')
+        expect(@doc).to have_selector('h3 a#autolinkfilter')
+      end
+    end
+
+    describe 'AutolinkFilter' do
+      let(:list) { get_section('autolinkfilter').parent.search('ul') }
+
+      def item(index)
+        list.at_css("li:nth-child(#{index})")
+      end
+
+      it 'autolinks http://' do
+        expect(item(1).children.first.name).to eq 'a'
+        expect(item(1).children.first['href']).to eq 'http://about.gitlab.com/'
+      end
+
+      it 'autolinks https://' do
+        expect(item(2).children.first.name).to eq 'a'
+        expect(item(2).children.first['href']).to eq 'https://google.com/'
+      end
+
+      it 'autolinks ftp://' do
+        expect(item(3).children.first.name).to eq 'a'
+        expect(item(3).children.first['href']).to eq 'ftp://ftp.us.debian.org/debian/'
+      end
+
+      it 'autolinks smb://' do
+        expect(item(4).children.first.name).to eq 'a'
+        expect(item(4).children.first['href']).to eq 'smb://foo/bar/baz'
+      end
+
+      it 'autolinks irc://' do
+        expect(item(5).children.first.name).to eq 'a'
+        expect(item(5).children.first['href']).to eq 'irc://irc.freenode.net/git'
+      end
+
+      it 'autolinks short, invalid URLs' do
+        expect(item(6).children.first.name).to eq 'a'
+        expect(item(6).children.first['href']).to eq 'http://localhost:3000'
+      end
+
+      %w(code a kbd).each do |elem|
+        it "ignores links inside '#{elem}' element" do
+          expect(@doc.at_css("#{elem}#autolink-#{elem}").child).to be_text
+        end
+      end
+    end
+
+    describe 'ReferenceFilter' do
+      it 'handles references in headers' do
+        header = @doc.at_css('#reference-filters-eg-1').parent
+
+        expect(header.css('a').size).to eq 2
+      end
+
+      it "handles references in Markdown" do
+        body = get_section('reference-filters-eg-1')
+        expect(body).to have_selector('em a.gfm-merge_request', count: 1)
+      end
+
+      it 'parses user references' do
+        body = get_section('userreferencefilter')
+        expect(body).to have_selector('a.gfm.gfm-project_member', count: 3)
+      end
+
+      it 'parses issue references' do
+        body = get_section('issuereferencefilter')
+        expect(body).to have_selector('a.gfm.gfm-issue', count: 2)
+      end
+
+      it 'parses merge request references' do
+        body = get_section('mergerequestreferencefilter')
+        expect(body).to have_selector('a.gfm.gfm-merge_request', count: 2)
+      end
+
+      it 'parses snippet references' do
+        body = get_section('snippetreferencefilter')
+        expect(body).to have_selector('a.gfm.gfm-snippet', count: 2)
+      end
+
+      it 'parses commit range references' do
+        body = get_section('commitrangereferencefilter')
+        expect(body).to have_selector('a.gfm.gfm-commit_range', count: 2)
+      end
+
+      it 'parses commit references' do
+        body = get_section('commitreferencefilter')
+        expect(body).to have_selector('a.gfm.gfm-commit', count: 2)
+      end
+
+      it 'parses label references' do
+        body = get_section('labelreferencefilter')
+        expect(body).to have_selector('a.gfm.gfm-label', count: 3)
+      end
+    end
+  end
+end
+
+# This is a helper class used by the GitLab Markdown feature spec
+#
+# Because the feature spec only cares about the output of the Markdown, and the
+# test setup and teardown and parsing is fairly expensive, we only want to do it
+# once. Unfortunately RSpec will not let you access `let`s in a `before(:all)`
+# block, so we fake it by encapsulating all the shared setup in this class.
+#
+# The class contains the raw Markup used in the test, dynamically substituting
+# real objects, created from factories and setup on-demand, when referenced in
+# the Markdown.
+class MarkdownFeature
+  include FactoryGirl::Syntax::Methods
+
+  def initialize
+    DatabaseCleaner.start
+  end
+
+  def teardown
+    DatabaseCleaner.clean
+  end
+
+  def user
+    @user ||= create(:user)
+  end
+
+  def group
+    unless @group
+      @group = create(:group)
+      @group.add_user(user, Gitlab::Access::DEVELOPER)
+    end
+
+    @group
+  end
+
+  # Direct references ----------------------------------------------------------
+
+  def project
+    @project ||= create(:project)
+  end
+
+  def issue
+    @issue ||= create(:issue, project: project)
+  end
+
+  def merge_request
+    @merge_request ||= create(:merge_request, :simple, source_project: project)
+  end
+
+  def snippet
+    @snippet ||= create(:project_snippet, project: project)
+  end
+
+  def commit
+    @commit ||= project.repository.commit
+  end
+
+  def commit_range
+    unless @commit_range
+      commit2 = project.repository.commit('HEAD~3')
+      @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}")
+    end
+
+    @commit_range
+  end
+
+  def simple_label
+    @simple_label ||= create(:label, name: 'gfm', project: project)
+  end
+
+  def label
+    @label ||= create(:label, name: 'awaiting feedback', project: project)
+  end
+
+  # Cross-references -----------------------------------------------------------
+
+  def xproject
+    unless @xproject
+      namespace = create(:namespace, name: 'cross-reference')
+      @xproject = create(:project, namespace: namespace)
+      @xproject.team << [user, :developer]
+    end
+
+    @xproject
+  end
+
+  # Shortcut to "cross-reference/project"
+  def xref
+    xproject.path_with_namespace
+  end
+
+  def xissue
+    @xissue ||= create(:issue, project: xproject)
+  end
+
+  def xmerge_request
+    @xmerge_request ||= create(:merge_request, :simple, source_project: xproject)
+  end
+
+  def xsnippet
+    @xsnippet ||= create(:project_snippet, project: xproject)
+  end
+
+  def xcommit
+    @xcommit ||= xproject.repository.commit
+  end
+
+  def xcommit_range
+    unless @xcommit_range
+      xcommit2 = xproject.repository.commit('HEAD~2')
+      @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}")
+    end
+
+    @xcommit_range
+  end
+
+  def raw_markdown
+    fixture = Rails.root.join('spec/fixtures/markdown.md.erb')
+    ERB.new(File.read(fixture)).result(binding)
+  end
+end
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
new file mode 100644
index 0000000000000000000000000000000000000000..0c1407585572d3e57ce10af744c665c344daae7d
--- /dev/null
+++ b/spec/fixtures/markdown.md.erb
@@ -0,0 +1,178 @@
+# GitLab Markdown
+
+This document is intended to be a comprehensive example of custom GitLab
+Markdown usage. It will be parsed and then tested for accuracy. Let's get
+started.
+
+## Markdown
+
+GitLab uses [Redcarpet](http://git.io/ld_NVQ) to parse all Markdown into
+HTML.
+
+It has some special features. Let's try 'em out!
+
+### No Intra Emphasis
+
+This string should have no emphasis: foo_bar_baz
+
+### Tables
+
+| Header   | Row  | Example |
+| :------: | ---: | :------ |
+| Foo      | Bar  | **Baz** |
+
+### Fenced Code Blocks
+
+```c
+#include<stdio.h>
+
+main()
+{
+    printf("Hello World");
+
+}
+```
+
+```python
+print "Hello, World!"
+```
+
+### Strikethrough
+
+This text says this, ~~and this text doesn't~~.
+
+### Superscript
+
+This is my 1^(st) time using superscript in Markdown. Now this is my
+2^(nd).
+
+### Next step
+
+After the Markdown has been turned into HTML, it gets passed through...
+
+## HTML::Pipeline
+
+### SanitizationFilter
+
+GitLab uses <a href="http://git.io/vfW8a" class="sanitize" id="sanitize-link">HTML::Pipeline::SanitizationFilter</a>
+to sanitize the generated HTML, stripping dangerous or unwanted tags.
+
+Its default whitelist is pretty permissive. Check it:
+
+<b id="manual-b">This text is bold</b> and <em id="manual-em">this text is emphasized</em>.
+
+<code id="manual-code">echo "Hello, world!"</code>
+
+Press <kbd>s</kbd> to search.
+
+<strike>Emoji</strike> Plain old images! <img
+src="http://www.emoji-cheat-sheet.com/graphics/emojis/smile.png" width="20"
+height="20" id="manual-img" />
+
+Here comes a line break:
+
+<br id="manual-br" />
+
+And a horizontal rule:
+
+<hr id="manual-hr" />
+
+As permissive as it is, we've allowed even more stuff:
+
+<span class="light" id="span-class-light">Span elements</span>
+
+<a href="#" rel="nofollow" id="a-rel-nofollow">This is a link with a defined rel attribute, which should be removed</a>
+
+<a href="javascript:alert('Hi')" id="a-href-javascript">This is a link trying to be sneaky. It gets its link removed entirely.</a>
+
+### Escaping
+
+The problem with SanitizationFilter is that it can be too aggressive.
+
+| Input       | Expected         | Actual    |
+| ----------- | ---------------- | --------- |
+| `1 < 3 & 5` | 1 &lt; 3 &amp; 5 | 1 < 3 & 5 |
+| `<foo>`     | &lt;foo&gt;      | <foo>     |
+
+### EmojiFilter
+
+Because life would be :zzz: without Emoji, right? :rocket:
+
+Get ready for the Emoji :bomb:: :+1::-1::ok_hand::wave::v::raised_hand::muscle:
+
+### TableOfContentsFilter
+
+All headers in this document should be linkable. Try it.
+
+### AutolinkFilter
+
+These are all plain text that should get turned into links:
+
+- http://about.gitlab.com/
+- https://google.com/
+- ftp://ftp.us.debian.org/debian/
+- smb://foo/bar/baz
+- irc://irc.freenode.net/git
+- http://localhost:3000
+
+But it shouldn't autolink text inside certain tags:
+
+- <code id="autolink-code">http://about.gitlab.com/</code>
+- <a id="autolink-a">http://about.gitlab.com/</a>
+- <kbd id="autolink-kbd">http://about.gitlab.com/</kbd>
+
+### Reference Filters (e.g., #<%= issue.iid %>)
+
+References should be parseable even inside _!<%= merge_request.iid %>_ emphasis.
+
+#### UserReferenceFilter
+
+- All: @all
+- User: @<%= user.username %>
+- Group: @<%= group.name %>
+- Ignores invalid: @fake_user
+- Ignored in code: `@<%= user.username %>`
+- Ignored in links: [Link to @<%= user.username %>](#user-link)
+
+#### IssueReferenceFilter
+
+- Issue: #<%= issue.iid %>
+- Issue in another project: <%= xref %>#<%= xissue.iid %>
+- Ignored in code: `#<%= issue.iid %>`
+- Ignored in links: [Link to #<%= issue.iid %>](#issue-link)
+
+#### MergeRequestReferenceFilter
+
+- Merge request: !<%= merge_request.iid %>
+- Merge request in another project: <%= xref %>!<%= xmerge_request.iid %>
+- Ignored in code: `!<%= merge_request.iid %>`
+- Ignored in links: [Link to !<%= merge_request.iid %>](#merge-request-link)
+
+#### SnippetReferenceFilter
+
+- Snippet: $<%= snippet.id %>
+- Snippet in another project: <%= xref %>$<%= xsnippet.id %>
+- Ignored in code: `$<%= snippet.id %>`
+- Ignored in links: [Link to $<%= snippet.id %>](#snippet-link)
+
+#### CommitRangeReferenceFilter
+
+- Range: <%= commit_range %>
+- Range in another project: <%= xref %>@<%= xcommit_range %>
+- Ignored in code: `<%= commit_range %>`
+- Ignored in links: [Link to <%= commit_range %>](#commit-range-link)
+
+#### CommitReferenceFilter
+
+- Commit: <%= commit.id %>
+- Commit in another project: <%= xref %>@<%= xcommit.id %>
+- Ignored in code: `<%= commit.id %>`
+- Ignored in links: [Link to <%= commit.id %>](#commit-link)
+
+#### LabelReferenceFilter
+
+- Label by ID: ~<%= simple_label.id %>
+- Label by name: ~<%= simple_label.name %>
+- Label by name in quotes: ~"<%= label.name %>"
+- Ignored in code: `~<%= simple_label.name %>`
+- Ignored in links: [Link to ~<%= simple_label.id %>](#label-link)
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index e309dbb6a2f9a6429b8b35aaec9de3ac6802f7b2..b6be82e41096483ce568a0f54eb5f35e4cff8beb 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -107,8 +107,7 @@ describe GitlabMarkdownHelper do
       end
 
       it 'should not be confused by whitespace before bullets' do
-        rendered_text_asterisk = markdown(@source_text_asterisk,
-                                          parse_tasks: true)
+        rendered_text_asterisk = markdown(@source_text_asterisk, parse_tasks: true)
         rendered_text_dash = markdown(@source_text_dash, parse_tasks: true)
 
         expect(rendered_text_asterisk).to match(
@@ -207,78 +206,7 @@ describe GitlabMarkdownHelper do
   end
 
   describe "#markdown" do
-    # TODO (rspeicher) - This block tests multiple different contexts. Break this up!
-
-    it "should add ids and links to headers" do
-      # Test every rule except nested tags.
-      text = '..Ab_c-d. e..'
-      id = 'ab_c-d-e'
-      expect(markdown("# #{text}")).
-        to match(%r{<h1 id="#{id}">#{text}<a href="[^"]*##{id}"></a></h1>})
-      expect(markdown("# #{text}", {no_header_anchors:true})).
-      to eq("<h1>#{text}</h1>")
-
-      id = 'link-text'
-      expect(markdown("# [link text](url) ![img alt](url)")).to match(
-        %r{<h1 id="#{id}"><a href="[^"]*url">link text</a> <img[^>]*><a href="[^"]*##{id}"></a></h1>}
-      )
-    end
-
-    # REFERENCES (PART TWO: THE REVENGE) ---------------------------------------
-
-    it "should handle references in headers" do
-      actual = "\n# Working around ##{issue.iid}\n## Apply !#{merge_request.iid}"
-
-      expect(markdown(actual, no_header_anchors: true)).
-        to match(%r{<h1[^<]*>Working around <a.+>##{issue.iid}</a></h1>})
-      expect(markdown(actual, no_header_anchors: true)).
-        to match(%r{<h2[^<]*>Apply <a.+>!#{merge_request.iid}</a></h2>})
-    end
-
-    it "should handle references in <em>" do
-      actual = "Apply _!#{merge_request.iid}_ ASAP"
-
-      expect(markdown(actual)).
-        to match(%r{Apply <em><a.+>!#{merge_request.iid}</a></em>})
-    end
-
-    # CODE BLOCKS -------------------------------------------------------------
-
-    it "should leave code blocks untouched" do
-      allow(helper).to receive(:current_user).and_return(user)
-      allow(helper).to receive(:user_color_scheme_class).and_return(:white)
-
-      target_html = "<pre class=\"code highlight white plaintext\"><code>some code from $#{snippet.id}\nhere too\n</code></pre>\n"
-
-      expect(markdown("\n    some code from $#{snippet.id}\n    here too\n")).
-        to eq(target_html)
-      expect(markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n")).
-        to eq(target_html)
-    end
-
-    it "should leave inline code untouched" do
-      expect(markdown("Don't use `$#{snippet.id}` here.")).
-        to eq "<p>Don't use <code>$#{snippet.id}</code> here.</p>\n"
-    end
-
-    # REF-LIKE AUTOLINKS? -----------------------------------------------------
-    # Basically: Don't parse references inside `<a>` tags.
-
-    it "should leave ref-like autolinks untouched" do
-      expect(markdown("look at http://example.tld/#!#{merge_request.iid}")).to eq("<p>look at <a href=\"http://example.tld/#!#{merge_request.iid}\">http://example.tld/#!#{merge_request.iid}</a></p>\n")
-    end
-
-    it "should leave ref-like href of 'manual' links untouched" do
-      expect(markdown("why not [inspect !#{merge_request.iid}](http://example.tld/#!#{merge_request.iid})")).to eq("<p>why not <a href=\"http://example.tld/#!#{merge_request.iid}\">inspect </a><a href=\"#{namespace_project_merge_request_path(project.namespace, project, merge_request)}\" title=\"Merge Request: #{merge_request.title}\" class=\"gfm gfm-merge_request\">!#{merge_request.iid}</a><a href=\"http://example.tld/#!#{merge_request.iid}\"></a></p>\n")
-    end
-
-    it "should leave ref-like src of images untouched" do
-      expect(markdown("screen shot: ![some image](http://example.tld/#!#{merge_request.iid})")).to eq("<p>screen shot: <img src=\"http://example.tld/#!#{merge_request.iid}\" alt=\"some image\"></p>\n")
-    end
-
-    # RELATIVE URLS -----------------------------------------------------------
     # TODO (rspeicher): These belong in a relative link filter spec
-
     context 'relative links' do
       context 'with a valid repository' do
         before do
@@ -333,11 +261,6 @@ describe GitlabMarkdownHelper do
           expected = ""
           expect(markdown(actual)).to match(expected)
         end
-
-        it 'should allow whitelisted HTML tags from the user' do
-          actual = '<dl><dt>Term</dt><dd>Definition</dd></dl>'
-          expect(markdown(actual)).to match(actual)
-        end
       end
 
       context 'with an empty repository' do
@@ -353,34 +276,6 @@ describe GitlabMarkdownHelper do
         end
       end
     end
-
-    # SANITIZATION ------------------------------------------------------------
-    # TODO (rspeicher): These are testing SanitizationFilter, not `markdown`
-
-    it 'should sanitize tags that are not whitelisted' do
-      actual = '<textarea>no inputs allowed</textarea> <blink>no blinks</blink>'
-      expected = 'no inputs allowed no blinks'
-      expect(markdown(actual)).to match(expected)
-      expect(markdown(actual)).not_to match('<.textarea>')
-      expect(markdown(actual)).not_to match('<.blink>')
-    end
-
-    it 'should allow whitelisted tag attributes from the user' do
-      actual = '<a class="custom">link text</a>'
-      expect(markdown(actual)).to match(actual)
-    end
-
-    it 'should sanitize tag attributes that are not whitelisted' do
-      actual = '<a href="http://example.com/bar.html" foo="bar">link text</a>'
-      expected = '<a href="http://example.com/bar.html">link text</a>'
-      expect(markdown(actual)).to match(expected)
-    end
-
-    it 'should sanitize javascript in attributes' do
-      actual = %q(<a href="javascript:alert('foo')">link text</a>)
-      expected = '<a>link text</a>'
-      expect(markdown(actual)).to match(expected)
-    end
   end
 
   describe '#render_wiki_content' do
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 54dd8d4aa64dd87686e982499c9e3940f2c8f740..c08ddb4cae1bd6ba25dd6a110b8cb9cead4a67c6 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -5,24 +5,6 @@ describe IssuesHelper do
   let(:issue) { create :issue, project: project }
   let(:ext_project) { create :redmine_project }
 
-  describe "title_for_issue" do
-    it "should return issue title if used internal tracker" do
-      @project = project
-      expect(title_for_issue(issue.iid)).to eq issue.title
-    end
-
-    it "should always return empty string if used external tracker" do
-      @project = ext_project
-      expect(title_for_issue(rand(100))).to eq ""
-    end
-
-    it "should always return empty string if project nil" do
-      @project = nil
-
-      expect(title_for_issue(rand(100))).to eq ""
-    end
-  end
-
   describe "url_for_project_issues" do
     let(:project_url) { ext_project.external_issue_tracker.project_url }
     let(:ext_expected) do
diff --git a/spec/lib/gitlab/google_code_import/client_spec.rb b/spec/lib/gitlab/google_code_import/client_spec.rb
index d2bf871daa8bd1133375669d7a6e53c61258490d..a66b811e0fd1df3b48ee4f5ee82873fdc35caca5 100644
--- a/spec/lib/gitlab/google_code_import/client_spec.rb
+++ b/spec/lib/gitlab/google_code_import/client_spec.rb
@@ -23,6 +23,7 @@ describe Gitlab::GoogleCodeImport::Client do
   describe "#repos" do
     it "returns only Git repositories" do
       expect(subject.repos.length).to eq(1)
+      expect(subject.incompatible_repos.length).to eq(1)
     end
   end
 
diff --git a/spec/lib/gitlab/markdown/autolink_filter_spec.rb b/spec/lib/gitlab/markdown/autolink_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0bbdc11a9798f6b82c3a7f8632ac63e4f6fe09f8
--- /dev/null
+++ b/spec/lib/gitlab/markdown/autolink_filter_spec.rb
@@ -0,0 +1,106 @@
+require 'spec_helper'
+
+module Gitlab::Markdown
+  describe AutolinkFilter do
+    let(:link) { 'http://about.gitlab.com/' }
+
+    def filter(html, options = {})
+      described_class.call(html, options)
+    end
+
+    it 'does nothing when :autolink is false' do
+      exp = act = link
+      expect(filter(act, autolink: false).to_html).to eq exp
+    end
+
+    it 'does nothing with non-link text' do
+      exp = act = 'This text contains no links to autolink'
+      expect(filter(act).to_html).to eq exp
+    end
+
+    context 'Rinku schemes' do
+      it 'autolinks http' do
+        doc = filter("See #{link}")
+        expect(doc.at_css('a').text).to eq link
+        expect(doc.at_css('a')['href']).to eq link
+      end
+
+      it 'autolinks https' do
+        link = 'https://google.com/'
+        doc = filter("See #{link}")
+
+        expect(doc.at_css('a').text).to eq link
+        expect(doc.at_css('a')['href']).to eq link
+      end
+
+      it 'autolinks ftp' do
+        link = 'ftp://ftp.us.debian.org/debian/'
+        doc = filter("See #{link}")
+
+        expect(doc.at_css('a').text).to eq link
+        expect(doc.at_css('a')['href']).to eq link
+      end
+
+      it 'autolinks short URLs' do
+        link = 'http://localhost:3000/'
+        doc = filter("See #{link}")
+
+        expect(doc.at_css('a').text).to eq link
+        expect(doc.at_css('a')['href']).to eq link
+      end
+
+      it 'accepts link_attr options' do
+        doc = filter("See #{link}", link_attr: {class: 'custom'})
+
+        expect(doc.at_css('a')['class']).to eq 'custom'
+      end
+
+      described_class::IGNORE_PARENTS.each do |elem|
+        it "ignores valid links contained inside '#{elem}' element" do
+          exp = act = "<#{elem}>See #{link}</#{elem}>"
+          expect(filter(act).to_html).to eq exp
+        end
+      end
+    end
+
+    context 'other schemes' do
+      let(:link) { 'foo://bar.baz/' }
+
+      it 'autolinks smb' do
+        link = 'smb:///Volumes/shared/foo.pdf'
+        doc = filter("See #{link}")
+
+        expect(doc.at_css('a').text).to eq link
+        expect(doc.at_css('a')['href']).to eq link
+      end
+
+      it 'autolinks irc' do
+        link = 'irc://irc.freenode.net/git'
+        doc = filter("See #{link}")
+
+        expect(doc.at_css('a').text).to eq link
+        expect(doc.at_css('a')['href']).to eq link
+      end
+
+      it 'does not include trailing punctuation' do
+        doc = filter("See #{link}.")
+        expect(doc.at_css('a').text).to eq link
+
+        doc = filter("See #{link}, ok?")
+        expect(doc.at_css('a').text).to eq link
+      end
+
+      it 'accepts link_attr options' do
+        doc = filter("See #{link}", link_attr: {class: 'custom'})
+        expect(doc.at_css('a')['class']).to eq 'custom'
+      end
+
+      described_class::IGNORE_PARENTS.each do |elem|
+        it "ignores valid links contained inside '#{elem}' element" do
+          exp = act = "<#{elem}>See #{link}</#{elem}>"
+          expect(filter(act).to_html).to eq exp
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
index 27e930ef7daf3df4aae5a249808fa21158aada4f..b19bc125b92cc7eb406df611b9849f704fae3a11 100644
--- a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
@@ -8,29 +8,12 @@ module Gitlab::Markdown
       IssuesHelper
     end
 
-    let(:project) { create(:empty_project) }
+    let(:project) { create(:jira_project) }
     let(:issue)   { double('issue', iid: 123) }
 
     context 'JIRA issue references' do
       let(:reference) { "JIRA-#{issue.iid}" }
 
-      before do
-        jira = project.create_jira_service
-
-        props = {
-          'title'         => 'JIRA tracker',
-          'project_url'   => 'http://jira.example/issues/?jql=project=A',
-          'issues_url'    => 'http://jira.example/browse/:id',
-          'new_issue_url' => 'http://jira.example/secure/CreateIssue.jspa'
-        }
-
-        jira.update_attributes(properties: props, active: true)
-      end
-
-      after do
-        project.jira_service.destroy
-      end
-
       it 'requires project context' do
         expect { described_class.call('Issue JIRA-123', {}) }.
           to raise_error(ArgumentError, /:project/)
diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
index 393bf32e19610c632fd310b53af2e8f4e7920902..08382b3e7e87a2c718358423dfe159861f215ffb 100644
--- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
@@ -27,7 +27,7 @@ module Gitlab::Markdown
       let(:reference) { "##{issue.iid}" }
 
       it 'ignores valid references when using non-default tracker' do
-        expect(project).to receive(:issue_exists?).with(issue.iid).and_return(false)
+        expect(project).to receive(:get_issue).with(issue.iid).and_return(nil)
 
         exp = act = "Issue ##{issue.iid}"
         expect(filter(act).to_html).to eq exp
@@ -48,7 +48,7 @@ module Gitlab::Markdown
       it 'ignores invalid issue IDs' do
         exp = act = "Fixed ##{issue.iid + 1}"
 
-        expect(project).to receive(:issue_exists?).with(issue.iid + 1)
+        expect(project).to receive(:get_issue).with(issue.iid + 1).and_return(nil)
         expect(filter(act).to_html).to eq exp
       end
 
@@ -98,8 +98,8 @@ module Gitlab::Markdown
         before { allow_cross_reference! }
 
         it 'ignores valid references when cross-reference project uses external tracker' do
-          expect_any_instance_of(Project).to receive(:issue_exists?).
-            with(issue.iid).and_return(false)
+          expect_any_instance_of(Project).to receive(:get_issue).
+            with(issue.iid).and_return(nil)
 
           exp = act = "Issue ##{issue.iid}"
           expect(filter(act).to_html).to eq exp
diff --git a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ab909a686351ac469097b6176d0d0158cd81b126
--- /dev/null
+++ b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
@@ -0,0 +1,81 @@
+require 'spec_helper'
+
+module Gitlab::Markdown
+  describe SanitizationFilter do
+    def filter(html, options = {})
+      described_class.call(html, options)
+    end
+
+    describe 'default whitelist' do
+      it 'sanitizes tags that are not whitelisted' do
+        act = %q{<textarea>no inputs</textarea> and <blink>no blinks</blink>}
+        exp = 'no inputs and no blinks'
+        expect(filter(act).to_html).to eq exp
+      end
+
+      it 'sanitizes tag attributes' do
+        act = %q{<a href="http://example.com/bar.html" onclick="bar">Text</a>}
+        exp = %q{<a href="http://example.com/bar.html">Text</a>}
+        expect(filter(act).to_html).to eq exp
+      end
+
+      it 'sanitizes javascript in attributes' do
+        act = %q(<a href="javascript:alert('foo')">Text</a>)
+        exp = '<a>Text</a>'
+        expect(filter(act).to_html).to eq exp
+      end
+
+      it 'allows whitelisted HTML tags from the user' do
+        exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>"
+        expect(filter(act).to_html).to eq exp
+      end
+    end
+
+    describe 'custom whitelist' do
+      it 'allows `class` attribute on any element' do
+        exp = act = %q{<strong class="foo">Strong</strong>}
+        expect(filter(act).to_html).to eq exp
+      end
+
+      it 'allows `id` attribute on any element' do
+        exp = act = %q{<em id="foo">Emphasis</em>}
+        expect(filter(act).to_html).to eq exp
+      end
+
+      it 'allows `style` attribute on table elements' do
+        html = <<-HTML.strip_heredoc
+        <table>
+          <tr><th style="text-align: center">Head</th></tr>
+          <tr><td style="text-align: right">Body</th></tr>
+        </table>
+        HTML
+
+        doc = filter(html)
+
+        expect(doc.at_css('th')['style']).to eq 'text-align: center'
+        expect(doc.at_css('td')['style']).to eq 'text-align: right'
+      end
+
+      it 'allows `span` elements' do
+        exp = act = %q{<span>Hello</span>}
+        expect(filter(act).to_html).to eq exp
+      end
+
+      it 'removes `rel` attribute from `a` elements' do
+        doc = filter(%q{<a href="#" rel="nofollow">Link</a>})
+
+        expect(doc.css('a').size).to eq 1
+        expect(doc.at_css('a')['href']).to eq '#'
+        expect(doc.at_css('a')['rel']).to be_nil
+      end
+
+      it 'removes script-like `href` attribute from `a` elements' do
+        html = %q{<a href="javascript:alert('Hi')">Hi</a>}
+        doc = filter(html)
+
+        expect(doc.css('a').size).to eq 1
+        expect(doc.at_css('a')['href']).to be_nil
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb b/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f383a5850d5419ca307b45f317225d31f993d938
--- /dev/null
+++ b/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
@@ -0,0 +1,101 @@
+# encoding: UTF-8
+
+require 'spec_helper'
+
+module Gitlab::Markdown
+  describe TableOfContentsFilter do
+    def filter(html, options = {})
+      described_class.call(html, options)
+    end
+
+    def header(level, text)
+      "<h#{level}>#{text}</h#{level}>\n"
+    end
+
+    it 'does nothing when :no_header_anchors is truthy' do
+      exp = act = header(1, 'Header')
+      expect(filter(act, no_header_anchors: 1).to_html).to eq exp
+    end
+
+    it 'does nothing with empty headers' do
+      exp = act = header(1, nil)
+      expect(filter(act).to_html).to eq exp
+    end
+
+    1.upto(6) do |i|
+      it "processes h#{i} elements" do
+        html = header(i, "Header #{i}")
+        doc = filter(html)
+
+        expect(doc.css("h#{i} a").first.attr('id')).to eq "header-#{i}"
+      end
+    end
+
+    describe 'anchor tag' do
+      it 'has an `anchor` class' do
+        doc = filter(header(1, 'Header'))
+        expect(doc.css('h1 a').first.attr('class')).to eq 'anchor'
+      end
+
+      it 'links to the id' do
+        doc = filter(header(1, 'Header'))
+        expect(doc.css('h1 a').first.attr('href')).to eq '#header'
+      end
+
+      describe 'generated IDs' do
+        it 'translates spaces to dashes' do
+          doc = filter(header(1, 'This header has spaces in it'))
+          expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-has-spaces-in-it'
+        end
+
+        it 'squeezes multiple spaces and dashes' do
+          doc = filter(header(1, 'This---header     is poorly-formatted'))
+          expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-poorly-formatted'
+        end
+
+        it 'removes punctuation' do
+          doc = filter(header(1, "This, header! is, filled. with @ punctuation?"))
+          expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-filled-with-punctuation'
+        end
+
+        it 'appends a unique number to duplicates' do
+          doc = filter(header(1, 'One') + header(2, 'One'))
+
+          expect(doc.css('h1 a').first.attr('id')).to eq 'one'
+          expect(doc.css('h2 a').first.attr('id')).to eq 'one-1'
+        end
+
+        it 'supports Unicode' do
+          doc = filter(header(1, '한글'))
+          expect(doc.css('h1 a').first.attr('id')).to eq '한글'
+          expect(doc.css('h1 a').first.attr('href')).to eq '#한글'
+        end
+      end
+    end
+
+    describe 'result' do
+      def result(html)
+        HTML::Pipeline.new([described_class]).call(html)
+      end
+
+      let(:results) { result(header(1, 'Header 1') + header(2, 'Header 2')) }
+      let(:doc) { Nokogiri::XML::DocumentFragment.parse(results[:toc]) }
+
+      it 'is contained within a `ul` element' do
+        expect(doc.children.first.name).to eq 'ul'
+        expect(doc.children.first.attr('class')).to eq 'section-nav'
+      end
+
+      it 'contains an `li` element for each header' do
+        expect(doc.css('li').length).to eq 2
+
+        links = doc.css('li a')
+
+        expect(links.first.attr('href')).to eq '#header-1'
+        expect(links.first.text).to eq 'Header 1'
+        expect(links.last.attr('href')).to eq '#header-2'
+        expect(links.last.text).to eq 'Header 2'
+      end
+    end
+  end
+end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index d28b4768545e2d5196a4f08dc475cdd03ce720a1..dbcf7286e45907e87f4e56353d96fbfcebbbbcf1 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -11,9 +11,8 @@ describe Notify do
   let(:recipient) { create(:user, email: 'recipient@example.com') }
   let(:project) { create(:project) }
 
-  around(:each) { ActionMailer::Base.deliveries.clear }
-
   before(:each) do
+    ActionMailer::Base.deliveries.clear
     email = recipient.emails.create(email: "notifications@example.com")
     recipient.update_attribute(:notification_email, email.email)
   end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 879a63dd9f954585e08e46ce4618ce62e6dd84c5..37e21a9081835ad0b1cb7db750e83aad048c3f79 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -129,6 +129,48 @@ describe Project do
     end
   end
 
+  describe '#get_issue' do
+    let(:project) { create(:empty_project) }
+    let(:issue)   { create(:issue, project: project) }
+
+    context 'with default issues tracker' do
+      it 'returns an issue' do
+        expect(project.get_issue(issue.iid)).to eq issue
+      end
+
+      it 'returns nil when no issue found' do
+        expect(project.get_issue(999)).to be_nil
+      end
+    end
+
+    context 'with external issues tracker' do
+      before do
+        allow(project).to receive(:default_issues_tracker?).and_return(false)
+      end
+
+      it 'returns an ExternalIssue' do
+        issue = project.get_issue('FOO-1234')
+        expect(issue).to be_kind_of(ExternalIssue)
+        expect(issue.iid).to eq 'FOO-1234'
+        expect(issue.project).to eq project
+      end
+    end
+  end
+
+  describe '#issue_exists?' do
+    let(:project) { create(:empty_project) }
+
+    it 'is truthy when issue exists' do
+      expect(project).to receive(:get_issue).and_return(double)
+      expect(project.issue_exists?(1)).to be_truthy
+    end
+
+    it 'is falsey when issue does not exist' do
+      expect(project).to receive(:get_issue).and_return(nil)
+      expect(project.issue_exists?(1)).to be_falsey
+    end
+  end
+
   describe :update_merge_requests do
     let(:project) { create(:project) }
     let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
@@ -180,25 +222,6 @@ describe Project do
     end
   end
 
-  describe :issue_exists? do
-    let(:project) { create(:project) }
-    let(:existed_issue) { create(:issue, project: project) }
-    let(:not_existed_issue) { create(:issue) }
-    let(:ext_project) { create(:redmine_project) }
-
-    it 'should be true or if used internal tracker and issue exists' do
-      expect(project.issue_exists?(existed_issue.iid)).to be_truthy
-    end
-
-    it 'should be false or if used internal tracker and issue not exists' do
-      expect(project.issue_exists?(not_existed_issue.iid)).to be_falsey
-    end
-
-    it 'should always be true if used other tracker' do
-      expect(ext_project.issue_exists?(rand(100))).to be_truthy
-    end
-  end
-
   describe :default_issues_tracker? do
     let(:project) { create(:project) }
     let(:ext_project) { create(:redmine_project) }
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index d1844bd2b878e1003c538f49e5bd2873131f7745..6d4a806791012e9b6ab7a404d98c583e82095980 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -35,6 +35,7 @@ module TestEnv
 
     # Create repository for FactoryGirl.create(:project)
     setup_factory_repo
+
     # Create repository for FactoryGirl.create(:forked_project_with_submodules)
     setup_forked_repo
   end
@@ -54,7 +55,7 @@ module TestEnv
     tmp_test_path = Rails.root.join('tmp', 'tests', '**')
 
     Dir[tmp_test_path].each do |entry|
-      unless File.basename(entry) =~ /\Agitlab-(shell|test)\z/
+      unless File.basename(entry) =~ /\Agitlab-(shell|test|test-fork)\z/
         FileUtils.rm_rf(entry)
       end
     end