diff --git a/.gitignore b/.gitignore
index 2a97eacad489f02225935e3130421ec2c18bd1be..73bde4cc7615d532ec8c3fc0b6f5ce1a546ba553 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,7 +25,6 @@ config/initializers/rack_attack.rb
 config/initializers/smtp_settings.rb
 config/resque.yml
 config/unicorn.rb
-config/mail_room.yml
 config/secrets.yml
 coverage/*
 db/*.sqlite3
diff --git a/CHANGELOG b/CHANGELOG
index f274942f7b172af8f0d20a4fc0ef0896f92ca2bf..36612126c6712fb1a3e5fdf809139a58445c4b8b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,13 +1,91 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
-v 8.1.0 (unreleased)
+v 8.2.0 (unreleased)
+  - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
+  - Fix Drone CI service template not saving properly (Stan Hu)
+  - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749)
+  - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu)
+  - Improved performance of finding users by one of their Email addresses
+  - Add allow_failure field to commit status API (Stan Hu)
+  - Improved performance of replacing references in comments
+  - Show last project commit to default branch on project home page
+  - Highlight comment based on anchor in URL
+  - Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw)
+  - Improved performance of sorting milestone issues
+  - Allow users to select the Files view as default project view (Cristian Bica)
+  - Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy)
+  - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork
+  - Use git follow flag for commits page when retrieve history for file or directory
+  - Show merge request CI status on merge requests index page
+  - Send build name and stage in CI notification e-mail
+  - Extend yml syntax for only and except to support specifying repository path
+  - Enable shared runners to all new projects
+  - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
+  - Remove deprecated CI events from project settings page
+  - [API] Add ability to fetch the commit ID of the last commit that actually touched a file
+  - Add "New file" link to dropdown on project page
+  - Include commit logs in project search
+  - Add "added", "modified" and "removed" properties to commit object in webhook
+  - Rename "Back to" links to "Go to" because its not always a case it point to place user come from
+  - Allow groups to appear in the search results if the group owner allows it
+  - New design for project graphs page
+  - Fix incoming email config defaults
+  - MR target branch is now visible on a list view when it is different from project's default one
+  - Improve Continuous Integration graphs page
+
+v 8.1.4
+  - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
+  - Prevent redirect loop when home_page_url is set to the root URL
+  - Fix incoming email config defaults
+  - Make color of "Accept Merge Request" button consistent with current build status
+  - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
+
+v 8.1.3
+  - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
+  - Spread out runner contacted_at updates
+  - Use issue editor as cross reference comment author when issue is edited with a new mention
+  - Add Facebook authentication
+
+v 8.1.2
+  - Fix cloning Wiki repositories via HTTP (Stan Hu)
+  - Add migration to remove satellites directory
+  - Fix specific runners visibility
+  - Fix 500 when editing CI service
+  - Require CI jobs to be named
+  - Fix CSS for runner status
+  - Fix CI badge
+  - Allow developer to manage builds
+
+v 8.1.1
+  - Removed, see 8.1.2
+
+v 8.1.0
+  - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
+  - Fix duplicate repositories in GitHub import page (Stan Hu)
+  - Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
+  - Adds ability to create directories using the web editor (Ben Ford)
+  - Cleanup stuck CI builds
+  - Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
+  - Show notifications button when user is member of group rather than project (Grzegorz Bizon)
+  - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge.
+  - Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu)
+  - Don't show "Add README" link in an empty repository if user doesn't have access to push (Stan Hu)
+  - Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu)
+  - Speed up load times of issue detail pages by roughly 1.5x
+  - Fix CI rendering regressions
+  - If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg)
+  - Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu)
   - Make diff file view easier to use on mobile screens (Stan Hu)
+  - Improved performance of finding users by username or Email address
+  - Fix bug where merge request comments created by API would not trigger notifications (Stan Hu)
   - Add support for creating directories from Files page (Stan Hu)
   - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu)
   - Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu)
   - Improved performance of the trending projects page
+  - Remove CI migration task
   - Improved performance of finding projects by their namespace
   - Fix bug where transferring a project would result in stale commit links (Stan Hu)
+  - Fix build trace updating
   - Include full path of source and target branch names in New Merge Request page (Stan Hu)
   - Add user preference to view activities as default dashboard (Stan Hu)
   - Add option to admin area to sign in as a specific user (Pavel Forkert)
@@ -17,7 +95,10 @@ v 8.1.0 (unreleased)
   - Fix cases where Markdown did not render links in activity feed (Stan Hu)
   - Add first and last to pagination (Zeger-Jan van de Weg)
   - Added Commit Status API
+  - Added Builds View
+  - Added when to .gitlab-ci.yml
   - Show CI status on commit page
+  - Added CI_BUILD_TAG, _STAGE, _NAME and _TRIGGERED to CI builds
   - Show CI status on Your projects page and Starred projects page
   - Remove "Continuous Integration" page from dashboard
   - Add notes and SSL verification entries to hook APIs (Ben Boeckel)
@@ -27,6 +108,7 @@ v 8.1.0 (unreleased)
   - Move CI triggers page to project settings area
   - Move CI project settings page to CE project settings area
   - Fix bug when removed file was not appearing in merge request diff
+  - Show warning when build cannot be served by any of the available CI runners
   - Note the original location of a moved project when notifying users of the move
   - Improve error message when merging fails
   - Add support of multibyte characters in LDAP UID (Roman Petrov)
@@ -38,7 +120,7 @@ v 8.1.0 (unreleased)
   - Move CI web hooks page to project settings area
   - Fix User Identities API. It now allows you to properly create or update user's identities.
   - Add user preference to change layout width (Peter Göbel)
-  - Use commit status in merge request widget as preffered source of CI status
+  - Use commit status in merge request widget as preferred source of CI status
   - Integrate CI commit and build pages into project pages
   - Move CI services page to project settings area
   - Add "Quick Submit" behavior to input fields throughout the application. Use
@@ -46,6 +128,8 @@ v 8.1.0 (unreleased)
   - Fix position of hamburger in header for smaller screens (Han Loong Liauw)
   - Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji)
   - Persist filters when sorting on admin user page (Jerry Lukins)
+  - Update style of snippets pages (Han Loong Liauw)
+  - Allow dashboard and group issues/MRs to be filtered by label
   - Add spellcheck=false to certain input fields
   - Invalidate stored service password if the endpoint URL is changed
   - Project names are not fully shown if group name is too big, even on group page view
@@ -53,6 +137,15 @@ v 8.1.0 (unreleased)
   - Add "New Page" button to Wiki Pages tab (Stan Hu)
   - Only render 404 page from /public
   - Hide passwords from services API (Alex Lossent)
+  - Fix: Images cannot show when projects' path was changed
+  - Let gitlab-git-http-server generate and serve 'git archive' downloads
+  - Optimize query when filtering on issuables (Zeger-Jan van de Weg)
+  - Fix padding of outdated discussion item.
+  - Animate the logo on hover
+
+v 8.0.5
+  - Correct lookup-by-email for LDAP logins
+  - Fix loading spinner sometimes not being hidden on Merge Request tab switches
 
 v 8.0.4
   - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 69abadb151af13d00f8b900706bf686bdf0e7b47..9f79ff413a07f0bb181e3b8e9eb4ea162f9ba760 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -83,6 +83,7 @@ If you can, please submit a merge request with the fix or improvements including
 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submission
 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines](    doc/development/shell_commands.md).
 1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk.
+1. If your code creates new files on disk please read the [shared files guidelines](doc/development/shared_files.md).
 
 The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast.
 Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as regressions requiring patch releases.
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 57cf282ebbc41ec4cd51601733bc26d60c2341d4..338a5b5d8fec491b97978114dc35e36348fa56a7 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-2.6.5
+2.6.6
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
new file mode 100644
index 0000000000000000000000000000000000000000..9e11b32fcaa96816319e5d0dcff9fb2873f04061
--- /dev/null
+++ b/GITLAB_WORKHORSE_VERSION
@@ -0,0 +1 @@
+0.3.1
diff --git a/Gemfile b/Gemfile
index 967092994a670451318a7ea6e1b4540d16b25d03..c73aa26bd0a98f454f3cbb08634f2c2121887c20 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,13 +1,5 @@
 source "https://rubygems.org"
 
-def darwin_only(require_as)
-  RUBY_PLATFORM.include?('darwin') && require_as
-end
-
-def linux_only(require_as)
-  RUBY_PLATFORM.include?('linux') && require_as
-end
-
 gem 'rails', '4.1.12'
 
 # Specify a sprockets version due to security issue
@@ -27,6 +19,7 @@ gem 'devise-async',           '~> 0.9.0'
 gem 'doorkeeper',             '~> 2.1.3'
 gem 'omniauth',               '~> 1.2.2'
 gem 'omniauth-bitbucket',     '~> 0.0.2'
+gem 'omniauth-facebook',      '~> 3.0.0'
 gem 'omniauth-github',        '~> 1.1.1'
 gem 'omniauth-gitlab',        '~> 1.0.0'
 gem 'omniauth-google-oauth2', '~> 0.2.0'
@@ -47,7 +40,7 @@ gem "browser", '~> 1.0.0'
 
 # Extracting information from a git repository
 # Provide access to Gitlab::Git library
-gem "gitlab_git", '~> 7.2.18'
+gem "gitlab_git", '~> 7.2.20'
 
 # LDAP Auth
 # GitLab fork with several improvements to original library. For full list of changes
@@ -58,11 +51,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap"
 gem 'gollum-lib', '~> 4.0.2'
 
 # Language detection
-# GitLab fork of linguist does not require pygments/python dependency.
-# New version of original gem also dropped pygments support but it has strict
-# dependency to unstable rugged version. We have internal issue for replacing
-# fork with original gem when we meet on same rugged version - https://dev.gitlab.org/gitlab/gitlabhq/issues/2052.
-gem "gitlab-linguist", "~> 3.0.1", require: "linguist"
+gem "github-linguist", "~> 4.7.0", require: "linguist"
 
 # API
 gem 'grape',        '~> 0.6.1'
@@ -71,7 +60,7 @@ gem 'rack-cors',    '~> 0.4.0', require: 'rack/cors'
 
 # Format dates and times
 # based on human-friendly examples
-gem "stamp", '~> 0.5.0'
+gem "stamp", '~> 0.6.0'
 
 # Enumeration fields
 gem 'enumerize', '~> 0.7.0'
@@ -102,7 +91,7 @@ gem "seed-fu", '~> 2.3.5'
 gem 'html-pipeline', '~> 1.11.0'
 gem 'task_list',     '~> 1.0.2', require: 'task_list/railtie'
 gem 'github-markup', '~> 1.3.1'
-gem 'redcarpet',     '~> 3.3.2'
+gem 'redcarpet',     '~> 3.3.3'
 gem 'RedCloth',      '~> 4.2.9'
 gem 'rdoc',          '~>3.6'
 gem 'org-ruby',      '~> 0.9.12'
@@ -120,7 +109,7 @@ group :unicorn do
 end
 
 # State machine
-gem "state_machine", '~> 1.2.0'
+gem "state_machines-activerecord", '~> 0.3.0'
 # Run events after state machine commits
 gem 'after_commit_queue'
 
@@ -192,11 +181,11 @@ gem 'ace-rails-ap', '~> 2.0.1'
 gem 'mousetrap-rails', '~> 1.4.6'
 
 # Detect and convert string character encoding
-gem 'charlock_holmes', '~> 0.6.9.4'
+gem 'charlock_holmes', '~> 0.7.3'
 
 gem "sass-rails", '~> 4.0.5'
 gem "coffee-rails", '~> 4.1.0'
-gem "uglifier", '~> 2.3.2'
+gem "uglifier", '~> 2.7.2'
 gem 'turbolinks', '~> 2.5.0'
 gem 'jquery-turbolinks', '~> 2.0.1'
 
@@ -205,11 +194,11 @@ gem 'bootstrap-sass',     '~> 3.0'
 gem 'font-awesome-rails', '~> 4.2'
 gem 'gitlab_emoji',       '~> 0.1'
 gem 'gon',                '~> 5.0.0'
-gem 'jquery-atwho-rails', '~> 1.0.0'
+gem 'jquery-atwho-rails', '~> 1.3.2'
 gem 'jquery-rails',       '~> 3.1.3'
 gem 'jquery-scrollto-rails', '~> 1.4.3'
 gem 'jquery-ui-rails',    '~> 4.2.1'
-gem 'nprogress-rails',    '~> 0.1.2.3'
+gem 'nprogress-rails',    '~> 0.1.6.7'
 gem 'raphael-rails',      '~> 2.1.2'
 gem 'request_store',      '~> 1.2.0'
 gem 'select2-rails',      '~> 3.5.9'
@@ -222,8 +211,9 @@ group :development do
   gem "annotate", "~> 2.6.0"
   gem "letter_opener", '~> 1.1.2'
   gem 'quiet_assets', '~> 1.0.2'
-  gem 'rack-mini-profiler', '~> 0.9.0', require: false
   gem 'rerun', '~> 0.10.0'
+  gem 'bullet', require: false
+  gem 'rblineprof', platform: :mri, require: false
 
   # Better errors handler
   gem 'better_errors', '~> 1.0.1'
@@ -290,7 +280,7 @@ gem 'newrelic-grape'
 
 gem 'octokit', '~> 3.7.0'
 
-gem "mail_room", "~> 0.6.0"
+gem "mail_room", "~> 0.6.1"
 
 gem 'email_reply_parser', '~> 0.5.8'
 
@@ -304,11 +294,3 @@ gem 'oauth2', '~> 1.0.0'
 
 # Soft deletion
 gem "paranoia", "~> 2.0"
-
-group :development, :test do
-  gem 'guard-rspec', '~> 4.2.0'
-
-  gem 'rb-fsevent', require: darwin_only('rb-fsevent')
-  gem 'growl',      require: darwin_only('growl')
-  gem 'rb-inotify', require: linux_only('rb-inotify')
-end
diff --git a/Gemfile.lock b/Gemfile.lock
index 58426a606836a12140e7e52a36573accf8fbdb4d..dca8606806a8072a1905581b45530a3dc7d35fba 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -87,6 +87,9 @@ GEM
       terminal-table (~> 1.4)
     browser (1.0.0)
     builder (3.2.2)
+    bullet (4.14.9)
+      activesupport (>= 3.0.0)
+      uniform_notifier (~> 1.9.0)
     byebug (6.0.2)
     cal-heatmap-rails (0.0.1)
     capybara (2.4.4)
@@ -104,7 +107,7 @@ GEM
       json (>= 1.7)
     celluloid (0.16.0)
       timers (~> 4.0.0)
-    charlock_holmes (0.6.9.4)
+    charlock_holmes (0.7.3)
     chunky_png (1.3.4)
     cliver (0.3.2)
     coderay (1.1.0)
@@ -134,6 +137,7 @@ GEM
     daemons (1.2.3)
     database_cleaner (1.4.1)
     debug_inspector (0.0.2)
+    debugger-ruby_core_source (1.3.8)
     default_value_for (3.0.1)
       activerecord (>= 3.2.0, < 5.0)
     descendants_tracker (0.0.4)
@@ -171,7 +175,7 @@ GEM
       activesupport (>= 3.2)
     equalizer (0.0.11)
     erubis (2.7.0)
-    escape_utils (0.2.4)
+    escape_utils (1.1.0)
     eventmachine (1.0.8)
     excon (0.45.4)
     execjs (2.6.0)
@@ -262,6 +266,11 @@ GEM
       json
     get_process_mem (0.2.0)
     gherkin-ruby (0.3.2)
+    github-linguist (4.7.0)
+      charlock_holmes (~> 0.7.3)
+      escape_utils (~> 1.1.0)
+      mime-types (>= 1.19)
+      rugged (>= 0.23.0b)
     github-markup (1.3.3)
     gitlab-flowdock-git-hook (1.0.1)
       flowdock (~> 0.7)
@@ -272,17 +281,13 @@ GEM
       diff-lcs (~> 1.1)
       mime-types (~> 1.15)
       posix-spawn (~> 0.3)
-    gitlab-linguist (3.0.1)
-      charlock_holmes (~> 0.6.6)
-      escape_utils (~> 0.2.4)
-      mime-types (~> 1.19)
     gitlab_emoji (0.1.1)
       gemojione (~> 2.0)
-    gitlab_git (7.2.18)
+    gitlab_git (7.2.20)
       activesupport (~> 4.0)
-      charlock_holmes (~> 0.6)
-      gitlab-linguist (~> 3.0)
-      rugged (~> 0.22.2)
+      charlock_holmes (~> 0.7.3)
+      github-linguist (~> 4.7.0)
+      rugged (~> 0.23.3)
     gitlab_meta (7.0)
     gitlab_omniauth-ldap (1.2.1)
       net-ldap (~> 0.9)
@@ -314,19 +319,6 @@ GEM
     grape-entity (0.4.8)
       activesupport
       multi_json (>= 1.3.2)
-    growl (1.0.3)
-    guard (2.13.0)
-      formatador (>= 0.2.4)
-      listen (>= 2.7, <= 4.0)
-      lumberjack (~> 1.0)
-      nenv (~> 0.1)
-      notiffany (~> 0.0)
-      pry (>= 0.9.12)
-      shellany (~> 0.0)
-      thor (>= 0.18.1)
-    guard-rspec (4.2.10)
-      guard (~> 2.1)
-      rspec (>= 2.14, < 4.0)
     haml (4.0.7)
       tilt
     haml-rails (0.9.0)
@@ -362,7 +354,7 @@ GEM
     ice_nine (0.11.1)
     inflecto (0.0.2)
     ipaddress (0.8.0)
-    jquery-atwho-rails (1.0.1)
+    jquery-atwho-rails (1.3.2)
     jquery-rails (3.1.3)
       railties (>= 3.0, < 5.0)
       thor (>= 0.14, < 2.0)
@@ -387,12 +379,11 @@ GEM
       celluloid (~> 0.16.0)
       rb-fsevent (>= 0.9.3)
       rb-inotify (>= 0.9)
-    lumberjack (1.0.9)
     macaddr (1.7.1)
       systemu (~> 2.6.2)
     mail (2.6.3)
       mime-types (>= 1.16, < 3)
-    mail_room (0.6.0)
+    mail_room (0.6.1)
     method_source (0.8.2)
     mime-types (1.25.1)
     mimemagic (0.3.0)
@@ -403,7 +394,6 @@ GEM
     multi_xml (0.5.5)
     multipart-post (2.0.0)
     mysql2 (0.3.20)
-    nenv (0.2.0)
     nested_form (0.3.2)
     net-ldap (0.11)
     net-scp (1.2.1)
@@ -416,10 +406,7 @@ GEM
     newrelic_rpm (3.9.4.245)
     nokogiri (1.6.6.2)
       mini_portile (~> 0.6.0)
-    notiffany (0.0.7)
-      nenv (~> 0.1)
-      shellany (~> 0.0)
-    nprogress-rails (0.1.2.3)
+    nprogress-rails (0.1.6.7)
     oauth (0.4.7)
     oauth2 (1.0.0)
       faraday (>= 0.8, < 0.10)
@@ -436,6 +423,8 @@ GEM
       multi_json (~> 1.7)
       omniauth (~> 1.1)
       omniauth-oauth (~> 1.0)
+    omniauth-facebook (3.0.0)
+      omniauth-oauth2 (~> 1.2)
     omniauth-github (1.1.2)
       omniauth (~> 1.0)
       omniauth-oauth2 (~> 1.1)
@@ -502,8 +491,6 @@ GEM
     rack-attack (4.3.0)
       rack
     rack-cors (0.4.0)
-    rack-mini-profiler (0.9.7)
-      rack (>= 1.1.3)
     rack-mount (0.8.3)
       rack (>= 1.0.0)
     rack-oauth2 (1.0.10)
@@ -540,13 +527,15 @@ GEM
     rb-fsevent (0.9.5)
     rb-inotify (0.9.5)
       ffi (>= 0.5.0)
+    rblineprof (0.3.6)
+      debugger-ruby_core_source (~> 1.3)
     rbvmomi (1.8.2)
       builder
       nokogiri (>= 1.4.1)
       trollop
     rdoc (3.12.2)
       json (~> 1.4)
-    redcarpet (3.3.2)
+    redcarpet (3.3.3)
     redis (3.2.1)
     redis-actionpack (4.0.0)
       actionpack (~> 4)
@@ -622,7 +611,7 @@ GEM
       sexp_processor (~> 4.1)
     rubyntlm (0.5.2)
     rubypants (0.2.0)
-    rugged (0.22.2)
+    rugged (0.23.3)
     safe_yaml (1.0.4)
     sanitize (2.1.0)
       nokogiri (>= 1.4.4)
@@ -647,7 +636,6 @@ GEM
     sexp_processor (4.6.0)
     sham_rack (1.3.6)
       rack
-    shellany (0.0.1)
     shoulda-matchers (2.8.0)
       activesupport (>= 3.0.0)
     sidekiq (3.3.0)
@@ -697,8 +685,14 @@ GEM
       actionpack (>= 3.0)
       activesupport (>= 3.0)
       sprockets (>= 2.8, < 4.0)
-    stamp (0.5.0)
-    state_machine (1.2.0)
+    stamp (0.6.0)
+    state_machines (0.4.0)
+    state_machines-activemodel (0.3.0)
+      activemodel (~> 4.1)
+      state_machines (>= 0.4.0)
+    state_machines-activerecord (0.3.0)
+      activerecord (~> 4.1)
+      state_machines-activemodel (>= 0.3.0)
     stringex (2.5.2)
     systemu (2.6.5)
     task_list (1.0.2)
@@ -741,7 +735,7 @@ GEM
       simple_oauth (~> 0.1.4)
     tzinfo (1.2.2)
       thread_safe (~> 0.1)
-    uglifier (2.3.3)
+    uglifier (2.7.2)
       execjs (>= 0.3.0)
       json (>= 1.8.0)
     underscore-rails (1.4.4)
@@ -755,6 +749,7 @@ GEM
     unicorn-worker-killer (0.4.3)
       get_process_mem (~> 0)
       unicorn (~> 4)
+    uniform_notifier (1.9.0)
     uuid (2.3.8)
       macaddr (~> 1.0)
     version_sorter (2.0.0)
@@ -800,12 +795,13 @@ DEPENDENCIES
   bootstrap-sass (~> 3.0)
   brakeman (= 3.0.1)
   browser (~> 1.0.0)
+  bullet
   byebug
   cal-heatmap-rails (~> 0.0.1)
   capybara (~> 2.4.0)
   capybara-screenshot (~> 1.0.0)
   carrierwave (~> 0.9.0)
-  charlock_holmes (~> 0.6.9.4)
+  charlock_holmes (~> 0.7.3)
   coffee-rails (~> 4.1.0)
   colored (~> 1.2)
   colorize (~> 0.5.8)
@@ -830,42 +826,41 @@ DEPENDENCIES
   foreman
   fuubar (~> 2.0.0)
   gemnasium-gitlab-service (~> 0.2)
+  github-linguist (~> 4.7.0)
   github-markup (~> 1.3.1)
   gitlab-flowdock-git-hook (~> 1.0.1)
-  gitlab-linguist (~> 3.0.1)
   gitlab_emoji (~> 0.1)
-  gitlab_git (~> 7.2.18)
+  gitlab_git (~> 7.2.20)
   gitlab_meta (= 7.0)
   gitlab_omniauth-ldap (~> 1.2.1)
   gollum-lib (~> 4.0.2)
   gon (~> 5.0.0)
   grape (~> 0.6.1)
   grape-entity (~> 0.4.2)
-  growl
-  guard-rspec (~> 4.2.0)
   haml-rails (~> 0.9.0)
   hipchat (~> 1.5.0)
   html-pipeline (~> 1.11.0)
   httparty (~> 0.13.3)
-  jquery-atwho-rails (~> 1.0.0)
+  jquery-atwho-rails (~> 1.3.2)
   jquery-rails (~> 3.1.3)
   jquery-scrollto-rails (~> 1.4.3)
   jquery-turbolinks (~> 2.0.1)
   jquery-ui-rails (~> 4.2.1)
   kaminari (~> 0.16.3)
   letter_opener (~> 1.1.2)
-  mail_room (~> 0.6.0)
+  mail_room (~> 0.6.1)
   minitest (~> 5.7.0)
   mousetrap-rails (~> 1.4.6)
   mysql2 (~> 0.3.16)
   nested_form (~> 0.3.2)
   newrelic-grape
   newrelic_rpm (~> 3.9.4.245)
-  nprogress-rails (~> 0.1.2.3)
+  nprogress-rails (~> 0.1.6.7)
   oauth2 (~> 1.0.0)
   octokit (~> 3.7.0)
   omniauth (~> 1.2.2)
   omniauth-bitbucket (~> 0.0.2)
+  omniauth-facebook (~> 3.0.0)
   omniauth-github (~> 1.1.1)
   omniauth-gitlab (~> 1.0.0)
   omniauth-google-oauth2 (~> 0.2.0)
@@ -882,14 +877,12 @@ DEPENDENCIES
   quiet_assets (~> 1.0.2)
   rack-attack (~> 4.3.0)
   rack-cors (~> 0.4.0)
-  rack-mini-profiler (~> 0.9.0)
   rack-oauth2 (~> 1.0.5)
   rails (= 4.1.12)
   raphael-rails (~> 2.1.2)
-  rb-fsevent
-  rb-inotify
+  rblineprof
   rdoc (~> 3.6)
-  redcarpet (~> 3.3.2)
+  redcarpet (~> 3.3.3)
   redis-rails (~> 4.0.0)
   request_store (~> 1.2.0)
   rerun (~> 0.10.0)
@@ -917,8 +910,8 @@ DEPENDENCIES
   spring-commands-spinach (~> 1.0.0)
   spring-commands-teaspoon (~> 0.0.2)
   sprockets (~> 2.12.3)
-  stamp (~> 0.5.0)
-  state_machine (~> 1.2.0)
+  stamp (~> 0.6.0)
+  state_machines-activerecord (~> 0.3.0)
   task_list (~> 1.0.2)
   teaspoon (~> 1.0.0)
   teaspoon-jasmine (~> 2.2.0)
@@ -926,7 +919,7 @@ DEPENDENCIES
   thin (~> 1.6.1)
   tinder (~> 1.10.0)
   turbolinks (~> 2.5.0)
-  uglifier (~> 2.3.2)
+  uglifier (~> 2.7.2)
   underscore-rails (~> 1.4.4)
   unf (~> 0.1.4)
   unicorn (~> 4.8.2)
diff --git a/PROCESS.md b/PROCESS.md
index 9f4b708d2b56b1c558e93e361284c802f707cba5..a4b0c83644b9a8c1d2a7d2313ea38c72ac5f1f77 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -115,3 +115,10 @@ We can only accept a merge request if all the tests are green. I've just
 restarted the build. When the tests are still not passing after this restart and
 you're sure that is does not have anything to do with your code changes, please
 rebase with master to see if that solves the issue.
+
+### Closing down the issue tracker on GitHub
+
+We are currently in the process of closing down the issue tracker on GitHub, to
+prevent duplication with the GitLab.com issue tracker.
+Since this is an older issue I'll be closing this for now. If you think this is
+still an issue I encourage you to open it on the \[GitLab.com issue tracker\](https://gitlab.com/gitlab-org/gitlab-ce/issues).
diff --git a/app/assets/fonts/SourceSansPro-Black.ttf b/app/assets/fonts/SourceSansPro-Black.ttf
old mode 100755
new mode 100644
index cb89a2d171e4db582ffaae8b5fe465680921996b..9c9b5cb7f03d4240a9ee0323aa2c4c4ec7689fda
Binary files a/app/assets/fonts/SourceSansPro-Black.ttf and b/app/assets/fonts/SourceSansPro-Black.ttf differ
diff --git a/app/assets/fonts/SourceSansPro-BlackIt.ttf b/app/assets/fonts/SourceSansPro-BlackIt.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..294ce5abe8f16d5e3576cf29759f0208017ad07c
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-BlackIt.ttf differ
diff --git a/app/assets/fonts/SourceSansPro-Bold.ttf b/app/assets/fonts/SourceSansPro-Bold.ttf
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-BoldIt.ttf b/app/assets/fonts/SourceSansPro-BoldIt.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..3decd130070e9b7bf549200aac16510eaee96923
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-BoldIt.ttf differ
diff --git a/app/assets/fonts/SourceSansPro-ExtraLight.ttf b/app/assets/fonts/SourceSansPro-ExtraLight.ttf
old mode 100755
new mode 100644
index bb4176c6fff742636a9ae8604cc22d2f21505bdf..253eafa3783744bab0c544e9f27833bc8bd9dab7
Binary files a/app/assets/fonts/SourceSansPro-ExtraLight.ttf and b/app/assets/fonts/SourceSansPro-ExtraLight.ttf differ
diff --git a/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..00d7e9a7aa81307a118a97acf7aeb2bbfba58c69
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf differ
diff --git a/app/assets/fonts/SourceSansPro-It.ttf b/app/assets/fonts/SourceSansPro-It.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..f7af53775951654ea308d497aadd200237d87e56
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-It.ttf differ
diff --git a/app/assets/fonts/SourceSansPro-Light.ttf b/app/assets/fonts/SourceSansPro-Light.ttf
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-LightIt.ttf b/app/assets/fonts/SourceSansPro-LightIt.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..f18827985ef836a047e5f292a5c5edb52189f2b5
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-LightIt.ttf differ
diff --git a/app/assets/fonts/SourceSansPro-Regular.ttf b/app/assets/fonts/SourceSansPro-Regular.ttf
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-Semibold.ttf b/app/assets/fonts/SourceSansPro-Semibold.ttf
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-SemiboldIt.ttf b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..13d66a1fc4555d0c15e291b6b0960469162ea467
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf differ
diff --git a/app/assets/images/auth_buttons/facebook_64.png b/app/assets/images/auth_buttons/facebook_64.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f1a80d7368ceb08b3e47d688f9e039b41ecfc0e
Binary files /dev/null and b/app/assets/images/auth_buttons/facebook_64.png differ
diff --git a/app/assets/images/logo.svg b/app/assets/images/logo.svg
index c09785cb96f49f6f59f1d62012016b477e75eae6..f4e19b67008ddfc7710d85858baa8dcf096fea2b 100644
--- a/app/assets/images/logo.svg
+++ b/app/assets/images/logo.svg
@@ -10,17 +10,17 @@
                 <g id="Fill-1-+-Group-24">
                     <g id="Group-24">
                         <g id="Group">
-                            <path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329"></path>
-                            <path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26"></path>
-                            <path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326"></path>
-                            <path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329"></path>
-                            <path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26"></path>
-                            <path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326"></path>
-                            <path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329"></path>
+                            <path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329" class="tanuki-shape"></path>
+                            <path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26" class="tanuki-shape"></path>
+                            <path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326" class="tanuki-shape"></path>
+                            <path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329" class="tanuki-shape"></path>
+                            <path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26" class="tanuki-shape"></path>
+                            <path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326" class="tanuki-shape"></path>
+                            <path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329" class="tanuki-shape"></path>
                         </g>
                     </g>
                 </g>
             </g>
         </g>
     </g>
-</svg>
\ No newline at end of file
+</svg>
diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee
index 050888f9c158325982a9513b7b47f612256fc5c2..f6bf836f19f3e4e594c4c077581ac6439371bde4 100644
--- a/app/assets/javascripts/blob/edit_blob.js.coffee
+++ b/app/assets/javascripts/blob/edit_blob.js.coffee
@@ -11,10 +11,10 @@ class @EditBlob
     if ace_mode
       editor.getSession().setMode "ace/mode/" + ace_mode
 
-    $(".js-commit-button").click ->
-      $("#file-content").val editor.getValue()
-      $(".file-editor form").submit()
-      return false
+    # Before a form submission, move the content from the Ace editor into the
+    # submitted textarea
+    $('form').submit ->
+      $("#file-content").val(editor.getValue())
 
     editModePanes = $(".js-edit-mode-pane")
     editModeLinks = $(".js-edit-mode a")
diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee
index 1f36a53f1917ce74ce32cb6aa69ec9e6ebc4f1fa..68c5e5195e396b5ef3a38ce187be52535037133c 100644
--- a/app/assets/javascripts/blob/new_blob.js.coffee
+++ b/app/assets/javascripts/blob/new_blob.js.coffee
@@ -11,10 +11,10 @@ class @NewBlob
     if ace_mode
       editor.getSession().setMode "ace/mode/" + ace_mode
 
-    $(".js-commit-button").click ->
-      $("#file-content").val editor.getValue()
-      $(".file-editor form").submit()
-      return false
+    # Before a form submission, move the content from the Ace editor into the
+    # submitted textarea
+    $('form').submit ->
+      $("#file-content").val(editor.getValue())
 
   editor: ->
     return @editor
diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee
index 4c4bc3d66edcd196f56c436bd2e89fa77290e2e4..976212369245616a4adc793ad8991b861a871d01 100644
--- a/app/assets/javascripts/calendar.js.coffee
+++ b/app/assets/javascripts/calendar.js.coffee
@@ -25,7 +25,7 @@ class @Calendar
         30
       ]
       legendCellPadding: 3
-      cellSize: $('.user-calendar').width() / 80
+      cellSize: $('.user-calendar').width() / 73
       onClick: (date, count) ->
         formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
         $.ajax
diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee
index c30859b484bd8bfe6fba1194b091d6b95c1563a4..44d5ddb7d951f39e2d65c47e539891892033fdd8 100644
--- a/app/assets/javascripts/ci/build.coffee
+++ b/app/assets/javascripts/ci/build.coffee
@@ -22,7 +22,7 @@ class CiBuild
       # Only valid for runnig build when output changes during time
       #
       CiBuild.interval = setInterval =>
-        if window.location.href is build_url
+        if window.location.href.split("#").first() is build_url
           $.ajax
             url: build_url
             dataType: "json"
@@ -31,7 +31,7 @@ class CiBuild
                 $('#build-trace code').html build.trace_html
                 $('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
                 @checkAutoscroll()
-              else
+              else if build.status != build_status
                 Turbolinks.visit build_url
       , 4000
 
diff --git a/app/assets/javascripts/copy_to_clipboard.js.coffee b/app/assets/javascripts/copy_to_clipboard.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..ec4b80cca6f51e5ac62e7061602e4c93d848a28b
--- /dev/null
+++ b/app/assets/javascripts/copy_to_clipboard.js.coffee
@@ -0,0 +1,21 @@
+#= require clipboard
+
+$ ->
+  clipboard = new Clipboard '.js-clipboard-trigger',
+    text: (trigger) ->
+      $target = $(trigger.nextElementSibling || trigger.previousElementSibling)
+      $target.data('clipboard-text') || $target.text().trim()
+
+  clipboard.on 'success', (e) ->
+    $(e.trigger).
+      tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!').
+      tooltip('show')
+
+    # Clear the selection and blur the trigger so it loses its border
+    e.clearSelection()
+    $(e.trigger).blur()
+
+    # Manually hide the tooltip after 1 second
+    setTimeout(->
+      $(e.trigger).tooltip('hide')
+    , 1000)
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 5bf0b302179f4cda666bf23647a33b90c1450e86..951173af5d5bef3dbed97fb6a277d6f1e7e04d5c 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -39,6 +39,12 @@ class Dispatcher
         shortcut_handler = new ShortcutsNavigation()
         new DropzoneInput($('.merge-request-form'))
         new IssuableForm($('.merge-request-form'))
+      when 'projects:tags:new'
+        new ZenMode()
+        new DropzoneInput($('.tag-form'))
+      when 'projects:releases:edit'
+        new ZenMode()
+        new DropzoneInput($('.release-form'))
       when 'projects:merge_requests:show'
         new Diff()
         shortcut_handler = new ShortcutsIssuable()
diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee
index 3e77ea515f819e36f3dca2fd0b641a25ab4cc267..593a8f42130d560598656814753d48df5fde3cde 100644
--- a/app/assets/javascripts/merge_request_tabs.js.coffee
+++ b/app/assets/javascripts/merge_request_tabs.js.coffee
@@ -68,8 +68,8 @@ class @MergeRequestTabs
 
   scrollToElement: (container) ->
     if window.location.hash
-      top = $(container + " " + window.location.hash).offset().top
-      $('body').scrollTo(top)
+      $el = $("#{container} #{window.location.hash}")
+      $('body').scrollTo($el.offset().top) if $el.length
 
   # Activate a tab based on the current action
   activateTab: (action) ->
@@ -127,7 +127,7 @@ class @MergeRequestTabs
         document.getElementById('commits').innerHTML = data.html
         $('.js-timeago').timeago()
         @commitsLoaded = true
-        @scrollToElement(".commits")
+        @scrollToElement("#commits")
 
   loadDiff: (source) ->
     return if @diffsLoaded
@@ -137,7 +137,7 @@ class @MergeRequestTabs
       success: (data) =>
         document.getElementById('diffs').innerHTML = data.html
         @diffsLoaded = true
-        @scrollToElement(".diffs")
+        @scrollToElement("#diffs")
 
   # Show or hide the loading spinner
   #
diff --git a/app/assets/javascripts/shortcuts_navigation.coffee b/app/assets/javascripts/shortcuts_navigation.coffee
index 5b6f9e7e3f25e3f75bdba43a28725cf8303ec722..8decaedd87bac29dc05d547dfbfb6ee48d2e341f 100644
--- a/app/assets/javascripts/shortcuts_navigation.coffee
+++ b/app/assets/javascripts/shortcuts_navigation.coffee
@@ -7,6 +7,7 @@ class @ShortcutsNavigation extends Shortcuts
     Mousetrap.bind('g e', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-project-activity'))
     Mousetrap.bind('g f', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-tree'))
     Mousetrap.bind('g c', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-commits'))
+    Mousetrap.bind('g b', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-builds'))
     Mousetrap.bind('g n', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-network'))
     Mousetrap.bind('g g', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-graphs'))
     Mousetrap.bind('g i', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-issues'))
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 6ce34b5c3e8f3c691dafa24dfa0900afc718b28c..1635df9c97b3081be3dcb1d7d1d742604c26328d 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -18,6 +18,7 @@
   line-height: 36px;
 }
 
+.content-block,
 .gray-content-block {
   margin: -$gl-padding;
   background-color: $background-color;
@@ -27,6 +28,14 @@
   border-bottom: 1px solid $border-color;
   color: $gl-gray;
 
+  &.oneline-block {
+    line-height: 42px;
+  }
+
+  &.white {
+    background-color: white;
+  }
+
   &.top-block {
     border-top: none;
   }
@@ -60,3 +69,48 @@
     line-height: 42px;
   }
 }
+
+.cover-block {
+  text-align: center;
+  background: #f7f8fa;
+  margin: -$gl-padding;
+  margin-bottom: 0;
+  padding: 44px $gl-padding;
+  border-bottom: 1px solid $border-color;
+  position: relative;
+
+  .avatar-holder {
+    margin-bottom: 16px;
+
+    .avatar, .identicon {
+      margin: 0 auto;
+      float: none;
+    }
+
+    .identicon {
+      @include border-radius(50%);
+    }
+  }
+
+  .cover-title {
+    color: $gl-header-color;
+    margin: 0;
+    font-size: 23px;
+    font-weight: normal;
+    margin: 16px 0 5px 0;
+    color: #4c4e54;
+    font-size: 23px;
+    line-height: 1.1;
+  }
+
+  .cover-desc {
+    padding: 0 $gl-padding 3px;
+    color: $gl-text-color;
+  }
+
+  .cover-controls {
+    position: absolute;
+    top: 10px;
+    right: 10px;
+  }
+}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index e5f0c0ad9eff29b73dd03d7bc005e03e760676db..fe56266284b9d5c5709ce5d2cdd0f2d00eb6226e 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -162,10 +162,25 @@
     border-color: #e7e9ed;
     width: 140px;
 
+    .badge {
+      font-weight: normal;
+      background-color: #eee;
+      color: #78a;
+    }
+
     &.active {
       border-color: $gl-info;
       background: $gl-info;
       color: #fff;
+
+      .badge {
+        color: $gl-info;
+        background-color: white;
+      }
     }
   }
 }
+
+.btn-clipboard {
+  border: none;
+}
diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss
index f1699d21c9b2919a02032ddcdd107c0558d7c511..f3ce4e3c2194787cb02a06785283718a66396ea1 100644
--- a/app/assets/stylesheets/framework/callout.scss
+++ b/app/assets/stylesheets/framework/callout.scss
@@ -9,9 +9,9 @@
 .bs-callout {
   margin: 20px 0;
   padding: 20px;
-  border-left: 3px solid #eee;
-  color: #666;
-  background: #f9f9f9;
+  border-left: 3px solid $border-color;
+  color: $text-color;
+  background: $background-color;
 }
 .bs-callout h4 {
   margin-top: 0;
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index e1a1793be9c1564e787fd3c1c2bd0876d2cf6626..ddbacd7fd4100cc4e80b005ec2d1e8992a95e9ba 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -16,6 +16,7 @@
 .append-bottom-10 { margin-bottom:10px }
 .append-bottom-15 { margin-bottom:15px }
 .append-bottom-20 { margin-bottom:20px }
+.append-bottom-default { margin-bottom: $gl-padding; }
 .inline { display: inline-block }
 .center { text-align: center }
 
@@ -387,6 +388,36 @@ table {
   }
 }
 
+.center-middle-menu {
+  @include nav-menu;
+  padding: 0;
+  text-align: center;
+  margin: -$gl-padding;
+  margin-top: 0;
+  margin-bottom: 0;
+  height: 58px;
+  border-bottom: 1px solid $border-color;
+
+  li {
+    &:after {
+      content: "|";
+      color: $border-gray-light;
+    }
+
+    &:last-child {
+      &:after {
+        content: none;
+      }
+    }
+
+    > a {
+      display: inline-block;
+      text-transform: uppercase;
+      font-size: 13px;
+    }
+  }
+}
+
 .dropzone .dz-preview .dz-progress {
   border-color: $border-color !important;
 }
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 9dd77747884d10b387268140401c5530b7012160..35db00281e5d26b055c65aac9432fd12ce68b996 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -10,6 +10,10 @@
   border-bottom: 1px solid #E7E9EE;
   margin-bottom: 1em;
 
+  &.readme-holder {
+    border-bottom: 0;
+  }
+
   table {
     @extend .table;
   }
@@ -94,7 +98,6 @@
           border-right: none;
         }
         background: #fff;
-        padding: 10px $gl-padding;
       }
       .lines {
         pre {
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index c7b3b60e76943a0fe77fde788b9b8529105cea3a..b91c15d8910f0b2fe2da7dca14863d9414685434 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -5,7 +5,6 @@ html {
 
   body {
     padding-top: $header-height;
-    text-rendering: geometricPrecision;
   }
 }
 
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index c5764c36597a9a4276c4d83f9b06cd548a09b338..45f3b5849bfc47cfb05d60790054838c58c232e5 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -107,7 +107,7 @@ ul.content-list {
 
   > li {
     padding: $gl-padding;
-    border-color: #f1f2f4;
+    border-color: $table-border-color;
     margin-left: -$gl-padding;
     margin-right: -$gl-padding;
     color: $gl-gray;
@@ -117,7 +117,7 @@ ul.content-list {
     }
 
     .controls {
-      padding-top: 4px;
+      padding-top: 1px;
       float: right;
 
       .btn {
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index ed0333d23368f5dd14393064496467c6d338d3e9..cc660529cb4d00e6a655bf76cd17f55b505b76e7 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -106,6 +106,7 @@
 }
 
 .markdown-area {
+  @include border-radius(0);
   background: #FFF;
   border: 1px solid #ddd;
   min-height: 140px;
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 089e6958eebef10cc353c4c208e4f718c27eb790..11c48d26ab53a27e50564b4c261ce0224653e6b0 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -72,9 +72,10 @@
   list-style: none;
 
   > li {
+    @include clearfix;
+
     padding: 10px 0;
     border-bottom: 1px solid #EEE;
-    overflow: hidden;
     display: block;
     margin: 0px;
 
@@ -137,6 +138,7 @@
 
       &:hover, &:active, &:focus {
         text-decoration: none;
+        outline: none;
       }
     }
 
@@ -147,14 +149,8 @@
 
     .badge {
       font-weight: normal;
-      background-color: #fff;
       background-color: #eee;
       color: #78a;
     }
   }
 }
-
-.fa-align {
-  top: 20px;
-  position: relative;
-}
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index cba621635b6eacb1b20c9288457ba14d38aca3a8..78fff58d2328dcf57e6d6f4fb462675e05803323 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -32,7 +32,7 @@
 }
 
 .select2-results .select2-result-label {
-  padding: 16px;
+  padding: 9px;
 }
 
 .select2-drop{
@@ -143,4 +143,4 @@
 
 .ajax-users-dropdown {
   min-width: 250px !important;
-}
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index c5ea3aca7caa80034d2e6550bab179abf860d003..c1b0129c866038faacc0f7cd7e315d90b63d339a 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -64,6 +64,7 @@
       text-decoration: none;
       padding-left: 22px;
       font-weight: normal;
+      outline: none;
 
       &:hover {
         text-decoration: none;
@@ -176,6 +177,7 @@
   text-align: center;
   line-height: 40px;
   transition-duration: .3s;
+  outline: none;
 }
 
 .collapse-nav a:hover {
@@ -238,10 +240,14 @@
       width: 100%;
       padding: 10px 22px;
       overflow: hidden;
+      outline: none;
 
       img {
         width: 36px;
         height: 36px;
+      }
+
+      #tanuki-logo, img {
         float: left;
       }
 
@@ -265,3 +271,13 @@
     }
   }
 }
+
+
+.tanuki-shape {
+  transition: all 0.8s;
+
+  &:hover {
+    fill: rgb(255, 255, 255);
+    transition: all 0.1s;
+  }
+}
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index 789b34020c18cc5729b9b1647e939ac0d692c474..66e16e8df75764e9838c46ee4e63d7d5e9df8e7f 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -1,3 +1,9 @@
+.table-holder {
+  margin: -$gl-padding;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
 table {
   &.table {
     .dropdown-menu a {
@@ -18,15 +24,17 @@ table {
 
     tr {
       td, th {
-        padding: 8px 10px;
+        padding: 10px $gl-padding;
         line-height: 20px;
         vertical-align: middle;
       }
+
       th {
         font-weight: normal;
         font-size: 15px;
         border-bottom: 1px solid $border-color !important;
       }
+
       td {
         border-color: $table-border-color !important;
         border-bottom: 1px solid;
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index bf21d7fce76d54e0af9e03205c1d1bc25d70afc9..eb53c4153d3e794e48443da57f213ae6315538cd 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -6,13 +6,17 @@
 
   .timeline-entry {
     padding: $gl-padding;
-    border-color: #f1f2f4;
+    border-color: $table-border-color;
     margin-left: -$gl-padding;
     margin-right: -$gl-padding;
     color: $gl-gray;
     border-bottom: 1px solid #ECEEF1;
     border-right: 1px solid #ECEEF1;
 
+    &:target {
+      background: $hover;
+    }
+
     &:last-child {
       border-bottom: none;
     }
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index bf36f96cc97f225c8735e202cf2b3248ef5828c2..ba0312ba0db5692549945a4f56dc7d928129e0ae 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -1,5 +1,6 @@
 @mixin md-typography {
   color: $md-text-color;
+  word-wrap: break-word;
 
   a {
     color: $md-link-color;
@@ -17,7 +18,6 @@
     font-family: $monospace_font;
     white-space: pre;
     word-wrap: normal;
-    padding: 1px 2px;
   }
 
   kbd {
@@ -73,6 +73,8 @@
   }
 
   blockquote {
+    color: #7f8fa4;
+    font-size: inherit;
     padding: 8px 21px;
     margin: 12px 0 12px;
     border-left: 3px solid #e7e9ed;
@@ -80,7 +82,7 @@
 
   blockquote p {
     color: #7f8fa4 !important;
-    font-size: 15px;
+    font-size: inherit;
     line-height: 1.5;
   }
 
@@ -101,9 +103,9 @@
 
   pre {
     margin: 12px 0 12px 0 !important;
-    background-color: #f8fafc !important;
+    background-color: #f8fafc;
     font-size: 13px !important;
-    color: #5b6169 !important;
+    color: #5b6169;
     line-height: 1.6em !important;
     @include border-radius(2px);
   }
@@ -112,9 +114,9 @@
     font-weight: inherit;
   }
 
-
-  ul {
-    color: #5c5d5e;
+  ul, ol {
+    padding: 0;
+    margin: 6px 0 6px 18px !important;
   }
 
   li {
@@ -136,6 +138,33 @@
       text-decoration: none;
     }
   }
+
+  /* Link to current header. */
+  h1, h2, h3, h4, h5, h6 {
+    position: relative;
+
+    a.anchor {
+      // Setting `display: none` would prevent the anchor being scrolled to, so
+      // instead we set the height to 0 and it gets updated on hover.
+      height: 0;
+    }
+
+    &:hover > a.anchor {
+      $size: 16px;
+      position: absolute;
+      right: 100%;
+      top: 50%;
+      margin-top: -$size/2;
+      margin-right: 0px;
+      padding-right: 20px;
+      display: inline-block;
+      width: $size;
+      height: $size;
+      background-image: image-url("icon-link.png");
+      background-size: contain;
+      background-repeat: no-repeat;
+    }
+  }
 }
 
 
@@ -144,7 +173,6 @@
  *
  */
 body {
-  text-rendering:optimizeLegibility;
   -webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px;
 }
 
@@ -202,53 +230,11 @@ a > code {
 }
 
 /**
- * Wiki typography
+ * Apply Markdown typography
  *
  */
 .wiki {
   @include md-typography;
-
-  word-wrap: break-word;
-  padding: 7px;
-
-  /* Link to current header. */
-  h1, h2, h3, h4, h5, h6 {
-    position: relative;
-
-    a.anchor {
-      // Setting `display: none` would prevent the anchor being scrolled to, so
-      // instead we set the height to 0 and it gets updated on hover.
-      height: 0;
-    }
-
-    &:hover > a.anchor {
-      $size: 16px;
-      position: absolute;
-      right: 100%;
-      top: 50%;
-      margin-top: -$size/2;
-      margin-right: 0px;
-      padding-right: 20px;
-      display: inline-block;
-      width: $size;
-      height: $size;
-      background-image: image-url("icon-link.png");
-      background-size: contain;
-      background-repeat: no-repeat;
-    }
-  }
-
-  ul,ol {
-    padding: 0;
-    margin: 6px 0 6px 18px !important;
-  }
-  ol {
-    color: #5c5d5e;
-  }
-}
-
-.md-area {
-  @include md-typography;
 }
 
 .md {
@@ -261,6 +247,7 @@ a > code {
  */
 textarea.js-gfm-input {
   font-family: $monospace_font;
+  color: $gl-text-color;
 }
 
 .md-preview {
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index 8323a8598ec7d546e363f7640e5d753f943a7df4..6a2b25ddc67905a29767100b831a84fe5a456be2 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -1,11 +1,10 @@
 /* https://github.com/MozMorris/tomorrow-pygments */
-pre.code.highlight.dark,
 .code.dark {
 
-  background-color: #1d1f21;
-  color: #c5c8c6;
+  background-color: #1d1f21 !important;
+  color: #c5c8c6 !important;
 
-  pre.code,
+  pre.highlight,
   .line-numbers,
   .line-numbers a {
     background-color: #1d1f21 !important;
@@ -23,8 +22,8 @@ pre.code.highlight.dark,
 
   // Search result highlight
   span.highlight_word {
-    background: #ffe792;
-    color: #000000;
+    background-color: #ffe792 !important;
+    color: #000000 !important;
   }
 
   .hll { background-color: #373b41 }
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index e83816743363941298c580cf677fb962cc4485a6..8560c3c490fc1e3070148bb05828d97bbfee3ced 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -1,15 +1,14 @@
 /* https://github.com/richleland/pygments-css/blob/master/monokai.css */
-pre.code.monokai,
 .code.monokai {
 
-  background: #272822;
-  color: #f8f8f2;
+  background-color: #272822 !important;
+  color: #f8f8f2 !important;
 
   pre.highlight,
   .line-numbers,
   .line-numbers a {
-    background:#272822 !important;
-    color:#f8f8f2 !important;
+    background-color :#272822 !important;
+    color: #f8f8f2 !important;
   }
 
   pre.code {
@@ -23,8 +22,8 @@ pre.code.monokai,
 
   // Search result highlight
   span.highlight_word {
-    background: #ffe792;
-    color: #000000;
+    background-color: #ffe792 !important;
+    color: #000000 !important;
   }
 
   .hll { background-color: #49483e }
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index bd41480aefb4706d6f7dc5ced47e501ec15c1611..7d489a9666b8b0828a5b7f4bf7670b0d6cdb23a4 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -1,11 +1,10 @@
 /* https://gist.github.com/qguv/7936275 */
-pre.code.highlight.solarized-dark,
 .code.solarized-dark {
 
-  background-color: #002b36;
-  color: #93a1a1;
+  background-color: #002b36 !important;
+  color: #93a1a1 !important;
 
-  pre.code,
+  pre.highlight,
   .line-numbers,
   .line-numbers a {
     background-color: #002b36 !important;
@@ -23,7 +22,7 @@ pre.code.highlight.solarized-dark,
 
   // Search result highlight
   span.highlight_word {
-    background: #094554;
+    background-color: #094554 !important;
   }
 
   /* Solarized Dark
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index 4cc62863870e701867d3bd67cd212c4dd44c0854..200ed346446e1dc110c09563a2a872914323f564 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -1,11 +1,10 @@
 /* https://gist.github.com/qguv/7936275 */
-pre.code.highlight.solarized-light,
 .code.solarized-light {
 
-  background-color: #fdf6e3;
-  color: #586e75;
+  background-color: #fdf6e3 !important;
+  color: #586e75 !important;
 
-  pre.code,
+  pre.highlight,
   .line-numbers,
   .line-numbers a {
     background-color: #fdf6e3 !important;
@@ -23,7 +22,7 @@ pre.code.highlight.solarized-light,
 
   // Search result highlight
   span.highlight_word {
-    background: #eee8d5;
+    background-color: #eee8d5 !important;
   }
 
   /* Solarized Light
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index 20a144ef9521c5e9fc3f95879a3c6b695c02aac4..e2626da78712858274a8fb233595f770ce765254 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -1,24 +1,20 @@
 /* https://github.com/aahan/pygments-github-style */
-pre.code.highlight.white,
 .code.white {
-  background-color: #f8fafc;
-  font-size: 13px;
-  color: #5b6169;
-  line-height: 1.6em;
 
+  background-color: #f8fafc !important;
+  color: #5b6169 !important;
+
+  pre.highlight,
   .line-numbers,
   .line-numbers a {
     background-color: $background-color !important;
     color: $gl-gray !important;
   }
 
-  pre.highlight {
-    background-color: #fff !important;
-    color: #333 !important;
-  }
-
   pre.code {
     border-left: 1px solid $border-color;
+    background-color: #fff !important;
+    color: #333 !important;
   }
 
   // highlight line via anchor
@@ -28,7 +24,7 @@ pre.code.highlight.white,
 
   // Search result highlight
   span.highlight_word {
-    background: #fafe3d;
+    background-color: #fafe3d !important;
   }
 
   .hll { background-color: #f8f8f8 }
diff --git a/app/assets/stylesheets/pages/ci_projects.scss b/app/assets/stylesheets/pages/ci_projects.scss
index 8c5273abcdaa07bf6ebf0a9c42cb567875ac04c6..2a7b5cfc7fd6c4a041118d747e445bedba4dbc30 100644
--- a/app/assets/stylesheets/pages/ci_projects.scss
+++ b/app/assets/stylesheets/pages/ci_projects.scss
@@ -6,11 +6,6 @@
     line-height: 1.5;
   }
 
-  .wide-table-holder {
-    margin-left: -$gl-padding;
-    margin-right: -$gl-padding;
-  }
-
   .builds,
   .projects-table {
     .light {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 4e121b95d13b14986749373eec8d66667eb3b998..c9dfcff62905997f8603e9ed49938503f77dbe62 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -33,6 +33,8 @@
 }
 
 li.commit {
+  list-style: none;
+
   .commit-row-title {
     font-size: $list-font-size;
     line-height: 20px;
@@ -113,3 +115,10 @@ li.commit {
     }
   }
 }
+
+.branch-commit {
+  color: $gl-gray;
+  .commit-id, .commit-row-message {
+    color: $gl-gray;
+  }
+}
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index d9ef06dc6b650ef7ea962a7ccac01f8a047aa664..afd6fb73675c36541b65e95f5a2a6b460c4656fe 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -367,7 +367,6 @@
 
 .inline-parallel-buttons {
   float: right;
-  margin-top: -5px;
 }
 
 // Mobile
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 1d565477dd40682cadb331ed22bc45b26ef5f81c..e2c521af91ec1dbce8e1135a86f44aa06d17b7ee 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -50,7 +50,7 @@
   .editor-file-name {
     .new-file-name {
       display: inline-block;
-      width: 200px;
+      width: 450px;
     }
 
     .form-control {
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index ca2ee455423b7b182097f4114920009043c57777..282aaf2219b97b54f029d673533c13bc454e780f 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -4,10 +4,10 @@
  */
 .event-item {
   font-size: $gl-font-size;
-  padding: $gl-padding;
+  padding: $gl-padding $gl-padding $gl-padding ($gl-padding + $gl-avatar-size + 15px);
   margin-left: -$gl-padding;
   margin-right: -$gl-padding;
-  border-bottom: 1px solid #f1f2f4;
+  border-bottom: 1px solid $table-border-color;
   color: #7f8fa4;
 
   &.event-inline {
@@ -16,10 +16,7 @@
       top: -2px;
     }
 
-    .event-title {
-      line-height: 44px;
-    }
-
+    .event-title,
     .event-item-timestamp {
       line-height: 44px;
     }
@@ -30,7 +27,7 @@
   }
 
   .avatar {
-    margin-right: 15px;
+    margin-left: -($gl-avatar-size + 15px);
   }
 
   .event-title {
@@ -43,8 +40,7 @@
   }
 
   .event-body {
-    margin-left: 63px;
-    margin-right: 80px;
+    margin-right: 174px;
 
     .event-note {
       margin-top: 5px;
@@ -155,6 +151,8 @@
 
 @media (max-width: $screen-xs-max) {
   .event-item {
+    padding-left: $gl-padding;
+
     .event-title {
       white-space: normal;
       overflow: visible;
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index 6da7a2511a2dd0b3c0f05f4e4c10c679f1936a43..bd224705f04aa29366c410542713eccabdfdc39b 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -68,3 +68,7 @@ body.modal-open {
 .modal .modal-dialog {
   width: 860px;
 }
+
+.documentation {
+  padding: 7px;
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 9da085a347337b7a7d7bf93232641049d4e2a606..abc27a19e3243e188245e4f9ad0901b2f1b71303 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -80,3 +80,24 @@
     }
   }
 }
+
+.issuable-filter-count {
+  span {
+    display: block;
+    margin-bottom: -16px;
+    padding: 13px 0;
+  }
+}
+
+.cross-project-reference {
+  text-align: center;
+  width: 100%;
+
+  .slead {
+    padding: 5px;
+  }
+
+  span, button {
+    background-color: $background-color;
+  }
+}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 4bf58cb4a590e3a02a69167416ce3d2299037a82..41c069f0ad375eeb49d38675ebe53676580cf976 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -132,6 +132,11 @@ form.edit-issue {
   }
 }
 
+.issue-closed-by-widget {
+  padding: 16px 0;
+  margin: 0px;
+}
+
 .issue-form .select2-container {
   width: 250px !important;
 }
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index a1a5208c59c29c555660fc6465a29c6320a48b3d..08e4bcdf529ad7354c21cdc5e1252dcac3187cef 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -19,6 +19,20 @@
   .accept-merge-holder {
     .accept-action {
       display: inline-block;
+
+      .accept_merge_request {
+        &.ci-pending,
+        &.ci-running {
+          @include btn-orange;
+        }
+
+        &.ci-skipped,
+        &.ci-failed,
+        &.ci-canceled,
+        &.ci-error {
+          @include btn-red;
+        }
+      }
     }
 
     .accept-control {
@@ -205,6 +219,15 @@
 
 #modal_merge_info .modal-dialog {
   width: 600px;
+
+  .btn-clipboard {
+    @extend .pull-right;
+
+    margin-right: 18px;
+    margin-top: 5px;
+    position: absolute;
+    right: 0;
+  }
 }
 
 .mr-source-target {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 4392f08942b6d939daecfe9ab670f8a955496adf..268fc995aa72a2d472afaa85077f478355bbd20e 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -56,6 +56,10 @@
   .note_text {
     width: 100%;
   }
+
+  .comment-hints {
+    margin-top: -12px;
+  }
 }
 
 /* loading indicator */
@@ -168,7 +172,7 @@
   color: #999;
   background: #FFF;
   padding: 7px;
-  margin-top: -11px;
+  margin-top: -7px;
   border: 1px solid $border-color;
   font-size: 13px;
 }
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index abb03b07f519484ee7afcee9e1173ea39ee808e1..1980fe0d4580e4f5d39bc87a6bfe35f2e2f79623 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -30,7 +30,6 @@ ul.notes {
   .discussion-header,
   .note-header {
     @extend .cgray;
-    padding-bottom: 15px;
 
     a:hover {
       text-decoration: none;
@@ -75,6 +74,10 @@ ul.notes {
     }
   }
 
+  .discussion-body {
+    padding-top: 15px;
+  }
+
   .discussion {
     overflow: hidden;
     display: block;
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 8e4f0eb2b252cff1d87a72518350b1f1bd9531fb..1d6ca0dfc1302663afdd50099bf7cf78a974aa7b 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -47,3 +47,31 @@
     }
   }
 }
+
+.calendar-hint {
+  margin-top: -12px;
+  float: right;
+  font-size: 12px;
+}
+
+.profile-link-holder {
+  display: inline;
+
+  &:after {
+    content: "\00B7";
+    padding: 0px 6px;
+    font-weight: bold;
+  }
+
+  &:last-child {
+    &:after {
+      content: "";
+      padding: 0;
+    }
+  }
+
+  a {
+    color: $blue-dark;
+    text-decoration: none;
+  }
+}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index f7a2284900398d3767ddf1814621f7e76f8a3fa5..d3b10040022e71ce86097277792e8d728dab8054 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -50,7 +50,17 @@
   }
 
   .project-home-dropdown {
-    margin: 11px 3px 0;
+    margin: 13px 0px 0;
+  }
+
+  .notifications-btn {
+    .fa-bell {
+      margin-right: 6px;
+    }
+
+    .fa-angle-down {
+      margin-left: 6px;
+    }
   }
 
   .project-home-desc {
@@ -85,6 +95,7 @@
       color: inherit;
     }
   }
+
   .input-group {
     display: inline-table;
     position: relative;
@@ -233,23 +244,11 @@
     }
   }
 
-  .fa-fw {
+  i {
     margin-right: 8px;
   }
 }
 
-.fa-bell {
-  margin-right: 6px;
-}
-
-.fa-angle-down {
-  margin-left: 6px;
-}
-
-.project-home-panel .project-home-dropdown {
-  margin: 13px 0px 0;
-}
-
 .project-visibility-level-holder {
   .radio {
     margin-bottom: 10px;
@@ -457,7 +456,7 @@ pre.light-well {
 
   .project-row {
     padding: $gl-padding;
-    border-color: #f1f2f4;
+    border-color: $table-border-color;
     margin-left: -$gl-padding;
     margin-right: -$gl-padding;
 
@@ -511,3 +510,46 @@ pre.light-well {
     margin-top: -1px;
   }
 }
+
+.project-last-commit {
+  margin: 0 7px;
+
+  .ci-status {
+    margin-right: 16px;
+  }
+
+  .commit-row-message {
+    color: $gl-gray;
+  }
+
+  .commit_short_id {
+    margin-right: 5px;
+    color: $gl-link-color;
+    font-weight: 600;
+  }
+
+  .commit-author-link {
+    margin-left: 7px;
+    text-decoration: none;
+    .avatar {
+      float: none;
+      margin-right: 4px;
+    }
+
+    .commit-author-name {
+      font-weight: 600;
+    }
+  }
+}
+
+.project-show-readme .readme-holder {
+  margin-left: -$gl-padding;
+  margin-right: -$gl-padding;
+  padding: ($gl-padding + 7px);
+  border-top: 0;
+
+  .edit-project-readme {
+    z-index: 100;
+    position: relative;
+  }
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss
index 2b15ab8312947d712810ab4a9552cee569af0136..a9111a7388fec3fb5cd8d5800b2f09803a7544de 100644
--- a/app/assets/stylesheets/pages/runners.scss
+++ b/app/assets/stylesheets/pages/runners.scss
@@ -1,36 +1,34 @@
-.ci-body {
-  .runner-state {
-    padding: 6px 12px;
-    margin-right: 10px;
-    color: #FFF;
+.runner-state {
+  padding: 6px 12px;
+  margin-right: 10px;
+  color: #FFF;
 
-    &.runner-state-shared {
-      background: #32b186;
-    }
-    &.runner-state-specific {
-      background: #3498db;
-    }
+  &.runner-state-shared {
+    background: #32b186;
   }
-
-  .runner-status-online {
-    color: green;
+  &.runner-state-specific {
+    background: #3498db;
   }
+}
 
-  .runner-status-offline {
-    color: gray;
-  }
+.runner-status-online {
+  color: green;
+}
 
-  .runner-status-paused {
-    color: red;
-  }
+.runner-status-offline {
+  color: gray;
+}
+
+.runner-status-paused {
+  color: red;
+}
 
-  .runner {
-    .btn {
-      padding: 1px 6px;
-    }
+.runner {
+  .btn {
+    padding: 1px 6px;
+  }
 
-    h4 {
-      font-weight: normal;
-    }
+  h4 {
+    font-weight: normal;
   }
 }
diff --git a/app/assets/stylesheets/pages/sherlock.scss b/app/assets/stylesheets/pages/sherlock.scss
new file mode 100644
index 0000000000000000000000000000000000000000..92d84d9640fc4cedf8d03bde1070ac7eda7eabf5
--- /dev/null
+++ b/app/assets/stylesheets/pages/sherlock.scss
@@ -0,0 +1,33 @@
+table .sherlock-code {
+  max-width: 700px;
+}
+
+.sherlock-code {
+  pre {
+    word-wrap: normal;
+  }
+
+  pre code {
+    white-space: pre;
+  }
+}
+
+.sherlock-line-samples-table {
+  margin-bottom: 0px !important;
+
+  thead tr th,
+  tbody tr td {
+    font-size: 13px !important;
+    text-align: right;
+    padding: 0px 10px !important;
+  }
+}
+
+.sherlock-file-sample pre {
+  padding-top: 28px !important;
+}
+
+.sherlock-line-samples-table .slow {
+  color: $red-light;
+  font-weight: bold;
+}
diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss
index a3d7aba054d21d654efe5152023b67910bb772d6..242783a7b7e13b46b94423017d4ba60457f86657 100644
--- a/app/assets/stylesheets/pages/snippets.scss
+++ b/app/assets/stylesheets/pages/snippets.scss
@@ -1,8 +1,3 @@
-.my-snippets li:first-child {
-  h4 { margin-top: 0; }
-  padding-top: 0;
-}
-
 .snippet-form-holder .file-holder .file-title {
   padding: 2px;
 }
@@ -30,3 +25,58 @@
     }
   }
 }
+
+.snippet-holder {
+  .snippet-details {
+    .page-title {
+      margin-top: -15px;
+      padding: 10px 0;
+      margin-bottom: 0;
+      color: #5c5d5e;
+      font-size: 16px;
+
+      .author {
+        color: #5c5d5e;
+      }
+
+      .snippet-id {
+        color: #5c5d5e;
+      }
+    }
+
+    .snippet-title {
+      margin: 0;
+      font-size: 23px;
+      color: #313236;
+    }
+
+    @media (max-width: $screen-md-max) {
+      .new-snippet-link {
+        display: none;
+      }
+    }
+
+    @media (max-width: $screen-sm-max) {
+      .creator,
+      .page-title .btn-close {
+        display: none;
+      }
+    }
+  }
+
+  .file-holder {
+    border-top: 0;
+  }
+}
+
+
+.snippet-box {
+  @include border-radius(2px);
+
+  display: inline-block;
+  padding: 10px $gl-padding;
+  font-weight: normal;
+  margin-right: 10px;
+  font-size: $gl-font-size;
+  border: 1px solid;
+}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index dadd86e88ccde1e062b438467d573f6c70044f95..d4ab6967ccde757e6cb6586368a11e29c9daad40 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,25 +1,11 @@
 .tree-holder {
-  .tree-table-holder {
-    margin-left: -$gl-padding;
-    margin-right: -$gl-padding;
-  }
-
-  .tree_progress {
-    display: none;
-    margin: 20px;
-    &.loading {
-      display: block;
-    }
-  }
 
   .tree-table {
     margin-bottom: 0;
 
     tr {
       > td, > th {
-        padding: 10px $gl-padding;
-        line-height: 32px;
-        border-color: $table-border-color !important;
+        line-height: 28px;
       }
 
       &:hover {
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb
index 65dbd5ef5512cb107e4871ba54bb0b3cf02630aa..2f4054eaa117e2fd1403905c77b2e894858dcde7 100644
--- a/app/controllers/abuse_reports_controller.rb
+++ b/app/controllers/abuse_reports_controller.rb
@@ -9,6 +9,10 @@ class AbuseReportsController < ApplicationController
     @abuse_report.reporter = current_user
 
     if @abuse_report.save
+      if current_application_settings.admin_notification_email.present?
+        AbuseReportMailer.delay.notify(@abuse_report.id)
+      end
+
       message = "Thank you for your report. A GitLab administrator will look into it shortly."
       redirect_to root_path, notice: message
     else
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 7c134d2ec9b7f30c28bf957e9d9f235d0ebefde2..3d9c59050ff11ee140557666436a2dc6ef80c8b1 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -55,7 +55,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
       :default_snippet_visibility,
       :restricted_signup_domains_raw,
       :version_check_enabled,
+      :admin_notification_email,
       :user_oauth_applications,
+      :shared_runners_enabled,
       restricted_visibility_levels: [],
       import_sources: []
     )
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
index 0808024fc39a46b39f49c298f7983a0080f39e6c..497c34f8f493bd9dfa31dc67656dbf5deb6b5151 100644
--- a/app/controllers/admin/broadcast_messages_controller.rb
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -19,7 +19,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
     BroadcastMessage.find(params[:id]).destroy
 
     respond_to do |format|
-      format.html { redirect_to :back }
+      format.html { redirect_back_or_default(default: { action: 'index' }) }
       format.js { render nothing: true }
     end
   end
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index d670386f8c62afdca3ef174da86df8dae1de4cba..0bd19c49d8f1f9a1e616ca9113049a47af614592 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -35,7 +35,7 @@ class Admin::HooksController < Admin::ApplicationController
     }
     @hook.execute(data, 'system_hooks')
 
-    redirect_to :back
+    redirect_back_or_default
   end
 
   def hook_params
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index a62170662e12c34070374c69f6396a616b82aeeb..461335883325e2c7b33b29c83114ce6ff880e5db 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -39,7 +39,13 @@ class Admin::ServicesController < Admin::ApplicationController
   end
 
   def application_services_params
-    params.permit(:id,
+    application_services_params = params.permit(:id,
       service: Projects::ServicesController::ALLOWED_PARAMS)
+    if application_services_params[:service].is_a?(Hash)
+      Projects::ServicesController::FILTER_BLANK_PARAMS.each do |param|
+        application_services_params[:service].delete(param) if application_services_params[:service][param].blank? 
+      end
+    end
+    application_services_params
   end
 end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 00f41a10dd11d093fa78e3b3e31b154cf28d7158..c63d0793e3141dde9c10d2fb500b374004d04d08 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -33,33 +33,33 @@ class Admin::UsersController < Admin::ApplicationController
 
   def block
     if user.block
-      redirect_to :back, notice: "Successfully blocked"
+      redirect_back_or_admin_user(notice: "Successfully blocked")
     else
-      redirect_to :back, alert: "Error occurred. User was not blocked"
+      redirect_back_or_admin_user(alert: "Error occurred. User was not blocked")
     end
   end
 
   def unblock
     if user.activate
-      redirect_to :back, notice: "Successfully unblocked"
+      redirect_back_or_admin_user(notice: "Successfully unblocked")
     else
-      redirect_to :back, alert: "Error occurred. User was not unblocked"
+      redirect_back_or_admin_user(alert: "Error occurred. User was not unblocked")
     end
   end
 
   def unlock
     if user.unlock_access!
-      redirect_to :back, alert: "Successfully unlocked"
+      redirect_back_or_admin_user(alert: "Successfully unlocked")
     else
-      redirect_to :back, alert: "Error occurred. User was not unlocked"
+      redirect_back_or_admin_user(alert: "Error occurred. User was not unlocked")
     end
   end
 
   def confirm
     if user.confirm
-      redirect_to :back, notice: "Successfully confirmed"
+      redirect_back_or_admin_user(notice: "Successfully confirmed")
     else
-      redirect_to :back, alert: "Error occurred. User was not confirmed"
+      redirect_back_or_admin_user(alert: "Error occurred. User was not confirmed")
     end
   end
 
@@ -138,7 +138,7 @@ class Admin::UsersController < Admin::ApplicationController
     user.update_secondary_emails!
 
     respond_to do |format|
-      format.html { redirect_to :back, notice: "Successfully removed email." }
+      format.html { redirect_back_or_admin_user(notice: "Successfully removed email.") }
       format.js { render nothing: true }
     end
   end
@@ -157,4 +157,12 @@ class Admin::UsersController < Admin::ApplicationController
       :projects_limit, :can_create_group, :admin, :key_id
     )
   end
+
+  def redirect_back_or_admin_user(options = {})
+    redirect_back_or_default(default: default_route, options: options)
+  end
+
+  def default_route
+    [:admin, @user]
+  end
 end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 2b2ea3dff16ecec10ece99c5eeab195dc75eb822..0d182e8eb04d92fded1152636459a9be917d82c1 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -33,6 +33,10 @@ class ApplicationController < ActionController::Base
     render_404
   end
 
+  def redirect_back_or_default(default: root_path, options: {})
+    redirect_to request.referer.present? ? :back : default, options
+  end
+
   protected
 
   # From https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example
@@ -55,13 +59,8 @@ class ApplicationController < ActionController::Base
   end
 
   def authenticate_user!(*args)
-    # If user is not signed-in and tries to access root_path - redirect him to landing page
-    # Don't redirect to the default URL to prevent endless redirections
-    if current_application_settings.home_page_url.present? &&
-        current_application_settings.home_page_url.chomp('/') != Gitlab.config.gitlab['url'].chomp('/')
-      if current_user.nil? && root_path == request.path
-        redirect_to current_application_settings.home_page_url and return
-      end
+    if redirect_to_home_page_url?
+      redirect_to current_application_settings.home_page_url and return
     end
 
     super(*args)
@@ -120,7 +119,6 @@ class ApplicationController < ActionController::Base
       project_path = "#{namespace}/#{id}"
       @project = Project.find_with_namespace(project_path)
 
-
       if @project and can?(current_user, :read_project, @project)
         if @project.path_with_namespace != project_path
           redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) and return
@@ -150,7 +148,7 @@ class ApplicationController < ActionController::Base
   end
 
   def git_not_found!
-    render "errors/git_not_found", layout: "errors", status: 404
+    render html: "errors/git_not_found", layout: "errors", status: 404
   end
 
   def method_missing(method_sym, *arguments, &block)
@@ -343,4 +341,17 @@ class ApplicationController < ActionController::Base
   def git_import_enabled?
     current_application_settings.import_sources.include?('git')
   end
+
+  def redirect_to_home_page_url?
+    # If user is not signed-in and tries to access root_path - redirect him to landing page
+    # Don't redirect to the default URL to prevent endless redirections
+    return false unless current_application_settings.home_page_url.present?
+
+    home_page_url = current_application_settings.home_page_url.chomp('/')
+    root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]
+
+    return false if root_urls.include?(home_page_url)
+
+    current_user.nil? && root_path == request.path
+  end
 end
diff --git a/app/controllers/ci/admin/runners_controller.rb b/app/controllers/ci/admin/runners_controller.rb
index 9a68add9083a46587ee0f51fbb2dfb7d4c64be4f..0cafad27418b043913aba0a843d9b888d07ada12 100644
--- a/app/controllers/ci/admin/runners_controller.rb
+++ b/app/controllers/ci/admin/runners_controller.rb
@@ -6,7 +6,7 @@ module Ci
       @runners = Ci::Runner.order('id DESC')
       @runners = @runners.search(params[:search]) if params[:search].present?
       @runners = @runners.page(params[:page]).per(30)
-      @active_runners_cnt = Ci::Runner.where("contacted_at > ?", 1.minutes.ago).count
+      @active_runners_cnt = Ci::Runner.online.count
     end
 
     def show
@@ -17,6 +17,7 @@ module Ci
         @projects = @projects.where(gitlab_id: @gl_projects.select(:id))
       end
       @projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any?
+      @projects = @projects.joins(:gl_project)
       @projects = @projects.page(params[:page]).per(30)
     end
 
@@ -66,7 +67,7 @@ module Ci
     end
 
     def runner_params
-      params.require(:runner).permit(:token, :description, :tag_list, :contacted_at, :active)
+      params.require(:runner).permit(:token, :description, :tag_list, :active)
     end
   end
 end
diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb
index 9be470660e6cd85f70fc6c235f6154c6a8b3520e..848f2b4e314e1eb4344bef0f99c4d91e15bff45d 100644
--- a/app/controllers/ci/application_controller.rb
+++ b/app/controllers/ci/application_controller.rb
@@ -8,14 +8,6 @@ module Ci
 
     private
 
-    def authenticate_public_page!
-      unless project.public
-        authenticate_user!
-
-        return access_denied! unless can?(current_user, :read_project, gl_project)
-      end
-    end
-
     def authenticate_token!
       unless project.valid_token?(params[:token])
         return head(403)
diff --git a/app/controllers/ci/events_controller.rb b/app/controllers/ci/events_controller.rb
deleted file mode 100644
index 89b784a1e8948177cb215512f59cdcd025653d4e..0000000000000000000000000000000000000000
--- a/app/controllers/ci/events_controller.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module Ci
-  class EventsController < Ci::ApplicationController
-    EVENTS_PER_PAGE = 50
-
-    before_action :authenticate_user!
-    before_action :project
-    before_action :authorize_manage_project!
-
-    layout 'ci/project'
-
-    def index
-      @events = project.events.order("created_at DESC").page(params[:page]).per(EVENTS_PER_PAGE)
-    end
-
-    private
-
-    def project
-      @project ||= Ci::Project.find(params[:project_id])
-    end
-  end
-end
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index 7777aa180316f0f806b07be1612cbbeabcffb699..809b44387ba0df263dc335101cc8dd884367a176 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -1,12 +1,17 @@
 module Ci
   class ProjectsController < Ci::ApplicationController
-    before_action :project
-    before_action :authenticate_user!, except: [:build, :badge]
-    before_action :authorize_access_project!, except: [:badge]
+    before_action :project, except: [:index]
+    before_action :authenticate_user!, except: [:index, :build, :badge]
+    before_action :authorize_access_project!, except: [:index, :badge]
     before_action :authorize_manage_project!, only: [:toggle_shared_runners, :dumped_yaml]
     before_action :no_cache, only: [:badge]
     protect_from_forgery
 
+    def show
+      # Temporary compatibility with CI badges pointing to CI project page
+      redirect_to namespace_project_path(project.gl_project.namespace, project.gl_project)
+    end
+
     # Project status badge
     # Image with build status for sha or ref
     def badge
diff --git a/app/controllers/ci/runner_projects_controller.rb b/app/controllers/ci/runner_projects_controller.rb
index 97f01d40af553dd3831dc2a83a39059626084d35..9d555313369a51c282d72624158d6a4f91fa4d9b 100644
--- a/app/controllers/ci/runner_projects_controller.rb
+++ b/app/controllers/ci/runner_projects_controller.rb
@@ -4,8 +4,6 @@ module Ci
     before_action :project
     before_action :authorize_manage_project!
 
-    layout 'ci/project'
-
     def create
       @runner = Ci::Runner.find(params[:runner_project][:runner_id])
 
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 40fb15a5b3682b50fc00e96c9e3766f54869889f..fb4eb094f274627c6e5c7a30348787f5d86d109f 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -4,12 +4,12 @@ class GroupsController < Groups::ApplicationController
   before_action :group, except: [:new, :create]
 
   # Authorize
-  before_action :authorize_read_group!, except: [:show, :new, :create]
+  before_action :authorize_read_group!, except: [:show, :new, :create, :autocomplete]
   before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects]
   before_action :authorize_create_group!, only: [:new, :create]
 
   # Load group projects
-  before_action :load_projects, except: [:new, :create, :projects, :edit, :update]
+  before_action :load_projects, except: [:new, :create, :projects, :edit, :update, :autocomplete]
   before_action :event_filter, only: :show
 
   layout :determine_layout
@@ -133,7 +133,7 @@ class GroupsController < Groups::ApplicationController
   end
 
   def group_params
-    params.require(:group).permit(:name, :description, :path, :avatar)
+    params.require(:group).permit(:name, :description, :path, :avatar, :public)
   end
 
   def load_events
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index aae77d384c6ae745e5851cc49fb8de2bceea3e55..67bf4190e7e66ca5b84fc27cb8cb4b96f378fab0 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -11,10 +11,6 @@ class Import::GithubController < Import::BaseController
 
   def status
     @repos = client.repos
-    client.orgs.each do |org|
-      @repos += client.org_repos(org.login)
-    end
-
     @already_added_projects = current_user.created_projects.where(import_type: "github")
     already_added_projects_names = @already_added_projects.pluck(:import_source)
 
diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb
index 41472a6fe6c16ba8dabb83751fc00ff05e521ac9..e0de31f225146f61172b8d7d1984552fda48f2f6 100644
--- a/app/controllers/import/google_code_controller.rb
+++ b/app/controllers/import/google_code_controller.rb
@@ -10,18 +10,18 @@ class Import::GoogleCodeController < Import::BaseController
     dump_file = params[:dump_file]
 
     unless dump_file.respond_to?(:read)
-      return redirect_to :back, alert: "You need to upload a Google Takeout archive."
+      return redirect_back_or_default(options: { alert: "You need to upload a Google Takeout archive." })
     end
 
     begin
       dump = JSON.parse(dump_file.read)
     rescue
-      return redirect_to :back, alert: "The uploaded file is not a valid Google Takeout archive."
+      return redirect_back_or_default(options: { alert: "The uploaded file is not a valid Google Takeout archive." })
     end
 
     client = Gitlab::GoogleCodeImport::Client.new(dump)
     unless client.valid?
-      return redirect_to :back, alert: "The uploaded file is not a valid Google Takeout archive."
+      return redirect_back_or_default(options: { alert: "The uploaded file is not a valid Google Takeout archive." })
     end
 
     session[:google_code_dump] = dump
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index 8ef10a17f5582cea6d55a210426c85f2c0b81a44..94bb108c5f58228de0f2c5c8883bf377e971ebcc 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -14,7 +14,7 @@ class InvitesController < ApplicationController
 
       redirect_to path, notice: "You have been granted #{member.human_access} access to #{label}."
     else
-      redirect_to :back, alert: "The invitation could not be accepted."
+      redirect_back_or_default(options: { alert: "The invitation could not be accepted." })
     end
   end
 
@@ -31,7 +31,7 @@ class InvitesController < ApplicationController
 
       redirect_to path, notice: "You have declined the invitation to join #{label}."
     else
-      redirect_to :back, alert: "The invitation could not be declined."
+      redirect_back_or_default(options: { alert: "The invitation could not be declined." })
     end
   end
 
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 22423651c1747c9928f32999f009fc78f7c0cf1f..1fd1d6882dfee07654367a6ad2272a073e319397 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -29,7 +29,7 @@ class Profiles::NotificationsController < Profiles::ApplicationController
           flash[:alert] = "Failed to save new settings"
         end
 
-        redirect_to :back
+        redirect_back_or_default(default: profile_notifications_path)
       end
 
       format.js
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 26a4de1546266b4f0e6197364c8254f9946fd6bc..8da7b4d50ea669faf70c3027b2a6d8c01cc593b6 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -26,7 +26,7 @@ class ProfilesController < Profiles::ApplicationController
     end
 
     respond_to do |format|
-      format.html { redirect_to :back }
+      format.html { redirect_back_or_default(default: { action: 'show' }) }
     end
   end
 
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 8cc2f21d8871ca974a4e8dce3e73b6d6cd67b920..93738aa1ee56eff11db08449cae911d948e20880 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -161,7 +161,7 @@ class Projects::BlobController < Projects::ApplicationController
         if params[:file].present?
           params[:file_name] = params[:file].original_filename
         end
-        File.join(@path, File.basename(params[:file_name]))
+        File.join(@path, params[:file_name])
       else
         @path
       end
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 4e4ac6689d327dafc4b7586651a5456919b9c67d..953f30e7c03a65bf523c87672dbfd69b4b8dca8b 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -1,14 +1,36 @@
 class Projects::BuildsController < Projects::ApplicationController
   before_action :ci_project
-  before_action :build
+  before_action :build, except: [:index, :cancel_all]
 
-  before_action :authorize_admin_project!, except: [:show, :status]
+  before_action :authorize_manage_builds!, except: [:index, :show, :status]
 
   layout "project"
 
+  def index
+    @scope = params[:scope]
+    @all_builds = project.ci_builds
+    @builds = @all_builds.order('created_at DESC')
+    @builds =
+      case @scope
+      when 'all'
+        @builds
+      when 'finished'
+        @builds.finished
+      else
+        @builds.running_or_pending.reverse_order
+      end
+    @builds = @builds.page(params[:page]).per(30)
+  end
+
+  def cancel_all
+    @project.ci_builds.running_or_pending.each(&:cancel)
+
+    redirect_to namespace_project_builds_path(project.namespace, project)
+  end
+
   def show
     @builds = @ci_project.commits.find_by_sha(@build.sha).builds.order('id DESC')
-    @builds = @builds.where("id not in (?)", @build.id).page(params[:page]).per(20)
+    @builds = @builds.where("id not in (?)", @build.id)
     @commit = @build.commit
 
     respond_to do |format|
@@ -20,17 +42,13 @@ class Projects::BuildsController < Projects::ApplicationController
   end
 
   def retry
-    if @build.commands.blank?
+    unless @build.retryable?
       return page_404
     end
 
     build = Ci::Build.retry(@build)
 
-    if params[:return_to]
-      redirect_to URI.parse(params[:return_to]).path
-    else
-      redirect_to build_path(build)
-    end
+    redirect_to build_path(build)
   end
 
   def status
@@ -52,4 +70,10 @@ class Projects::BuildsController < Projects::ApplicationController
   def build_path(build)
     namespace_project_build_path(build.gl_project.namespace, build.gl_project, build)
   end
+
+  def authorize_manage_builds!
+    unless can?(current_user, :manage_builds, project)
+      return page_404
+    end
+  end
 end
diff --git a/app/controllers/projects/ci_services_controller.rb b/app/controllers/projects/ci_services_controller.rb
index 6d2756eba3d10e5c22f19f89a1058230e0009d0e..550a019e8e29037f28a95a75a43585d674e81266 100644
--- a/app/controllers/projects/ci_services_controller.rb
+++ b/app/controllers/projects/ci_services_controller.rb
@@ -14,23 +14,23 @@ class Projects::CiServicesController < Projects::ApplicationController
   end
 
   def update
-    if @service.update_attributes(service_params)
-      redirect_to edit_namespace_project_ci_service_path(@project, @project.namespace, @service.to_param)
+    if service.update_attributes(service_params)
+      redirect_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param)
     else
       render 'edit'
     end
   end
 
   def test
-    last_build = @project.builds.last
+    last_build = @project.ci_builds.last
 
-    if @service.execute(last_build)
+    if service.execute(last_build)
       message = { notice: 'We successfully tested the service' }
     else
       message = { alert: 'We tried to test the service but error occurred' }
     end
 
-    redirect_to :back, message
+    redirect_back_or_default(options: message)
   end
 
   private
diff --git a/app/controllers/projects/ci_web_hooks_controller.rb b/app/controllers/projects/ci_web_hooks_controller.rb
index 7f40ddcb3f3124e2e3d107bc8a5eeedb827a5ff5..a2d470d4a6964143202f620acbf49d8bdb740f52 100644
--- a/app/controllers/projects/ci_web_hooks_controller.rb
+++ b/app/controllers/projects/ci_web_hooks_controller.rb
@@ -24,7 +24,7 @@ class Projects::CiWebHooksController < Projects::ApplicationController
   def test
     Ci::TestHookService.new.execute(hook, current_user)
 
-    redirect_to :back
+    redirect_back_or_default(default: { action: 'index' })
   end
 
   def destroy
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 7886f3c6deb20545a6231c026eb9bbffa64dff5c..deefdd766678ce6dc16999c3f4902c37755639bf 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -4,16 +4,17 @@
 class Projects::CommitController < Projects::ApplicationController
   # Authorize
   before_action :require_non_empty_project
-  before_action :authorize_download_code!
+  before_action :authorize_download_code!, except: [:cancel_builds]
+  before_action :authorize_manage_builds!, only: [:cancel_builds]
   before_action :commit
+  before_action :authorize_manage_builds!, only: [:cancel_builds, :retry_builds]
+  before_action :define_show_vars, only: [:show, :builds]
 
   def show
     return git_not_found! unless @commit
 
     @line_notes = commit.notes.inline
-    @diffs = @commit.diffs
     @note = @project.build_commit_note(commit)
-    @notes_count = commit.notes.count
     @notes = commit.notes.not_inline.fresh
     @noteable = @commit
     @comments_allowed = @reply_allowed = true
@@ -22,8 +23,6 @@ class Projects::CommitController < Projects::ApplicationController
       commit_id: @commit.id
     }
 
-    @ci_commit = project.ci_commit(commit.sha)
-
     respond_to do |format|
       format.html
       format.diff  { render text: @commit.to_diff }
@@ -31,20 +30,25 @@ class Projects::CommitController < Projects::ApplicationController
     end
   end
 
-  def ci
-    @ci_commit = @project.ci_commit(@commit.sha)
-    @builds = @ci_commit.builds if @ci_commit
-    @notes_count = @commit.notes.count
+  def builds
     @ci_project = @project.gitlab_ci_project
   end
 
   def cancel_builds
-    @ci_commit = @project.ci_commit(@commit.sha)
-    @ci_commit.builds.running_or_pending.each(&:cancel)
+    ci_commit.builds.running_or_pending.each(&:cancel)
 
-    redirect_to ci_namespace_project_commit_path(project.namespace, project, commit.sha)
+    redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
   end
 
+  def retry_builds
+    ci_commit.builds.latest.failed.each do |build|
+      if build.retryable?
+        Ci::Build.retry(build)
+      end
+    end
+
+    redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
+  end
 
   def branches
     @branches = @project.repository.branch_names_contains(commit.id)
@@ -52,7 +56,26 @@ class Projects::CommitController < Projects::ApplicationController
     render layout: false
   end
 
+  private
+
   def commit
     @commit ||= @project.commit(params[:id])
   end
+
+  def ci_commit
+    @ci_commit ||= project.ci_commit(commit.sha)
+  end
+
+  def define_show_vars
+    @diffs = commit.diffs
+    @notes_count = commit.notes.count
+    
+    @builds = ci_commit.builds if ci_commit
+  end
+
+  def authorize_manage_builds!
+    unless can?(current_user, :manage_builds, project)
+      return page_404
+    end
+  end
 end
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index d1c15174aea8ead1d12e15302fc17f04bbae3693..58fb946dbc24c8a3a06e663f9b9deddf38dde2fb 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -12,7 +12,7 @@ class Projects::CommitsController < Projects::ApplicationController
     @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
 
     @commits = @repo.commits(@ref, @path, @limit, @offset)
-    @note_counts = Note.where(commit_id: @commits.map(&:id)).
+    @note_counts = project.notes.where(commit_id: @commits.map(&:id)).
       group(:commit_id).count
 
     respond_to do |format|
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index d15004f93a613ff50a732433278343fa77fd765a..71aaad1fad61d565a67250afd6b55f1ad17bd900 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -17,9 +17,10 @@ class Projects::CompareController < Projects::ApplicationController
       execute(@project, head_ref, @project, base_ref)
 
     if compare_result
-      @commits = compare_result.commits
+      @commits = Commit.decorate(compare_result.commits, @project)
       @diffs = compare_result.diffs
       @commit = @commits.last
+      @first_commit = @commits.first
       @line_notes = []
     end
   end
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 40e2b37912bb4dd181cf858e5507f3cb8950dc01..7d09288bc8060a9045472167bc163b6409d5acd4 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -46,7 +46,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
   def disable
     @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy
 
-    redirect_to :back
+    redirect_back_or_default(default: { action: 'index' })
   end
 
   protected
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 4e5b4125f5a9cbaa654066ad9e1ae745c7cfeb2e..c7569541899602a7695188b326fd57df9256e102 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -37,7 +37,7 @@ class Projects::HooksController < Projects::ApplicationController
       flash[:alert] = 'Hook execution failed. Ensure the project has commits.'
     end
 
-    redirect_to :back
+    redirect_back_or_default(default: { action: 'index' })
   end
 
   def destroy
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 0f89f2e88ccce26ab6b5c225ad0b269993eb82f8..e767efbdc0cfd168c51670ac5b1b6fcad6b7feac 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -14,6 +14,9 @@ class Projects::IssuesController < Projects::ApplicationController
   # Allow issues bulk update
   before_action :authorize_admin_issues!, only: [:bulk_update]
 
+  # Cross-reference merge requests
+  before_action :closed_by_merge_requests, only: [:show]
+
   respond_to :html
 
   def index
@@ -55,9 +58,9 @@ class Projects::IssuesController < Projects::ApplicationController
   end
 
   def show
-    @participants = @issue.participants(current_user, @project)
+    @participants = @issue.participants(current_user)
     @note = @project.notes.new(noteable: @issue)
-    @notes = @issue.notes.inc_author.fresh
+    @notes = @issue.notes.with_associations.fresh
     @noteable = @issue
 
     respond_with(@issue)
@@ -103,7 +106,7 @@ class Projects::IssuesController < Projects::ApplicationController
 
   def bulk_update
     result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute
-    redirect_to :back, notice: "#{result[:count]} issues updated"
+    redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" })
   end
 
   def toggle_subscription
@@ -112,6 +115,10 @@ class Projects::IssuesController < Projects::ApplicationController
     render nothing: true
   end
 
+  def closed_by_merge_requests
+    @closed_by_merge_requests ||= @issue.closed_by_merge_requests(current_user)
+  end
+
   protected
 
   def issue
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 7570934e7277c18c0cae84997e5754bc8bfa0c62..b0788a2d073e8ceca450aa50872cecd50cadcfb6 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -31,6 +31,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     end
 
     @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
+    @merge_requests = @merge_requests.preload(:target_project)
 
     respond_to do |format|
       format.html
@@ -56,6 +57,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 
   def diffs
     @commit = @merge_request.last_commit
+    @first_commit = @merge_request.first_commit
+
     @comments_allowed = @reply_allowed = true
     @comments_target = {
       noteable_type: 'MergeRequest',
@@ -89,7 +92,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     @target_project = merge_request.target_project
     @source_project = merge_request.source_project
     @commits = @merge_request.compare_commits
-    @commit = @merge_request.compare_commits.last
+    @commit = @merge_request.last_commit
+    @first_commit = @merge_request.first_commit
     @diffs = @merge_request.compare_diffs
     @note_counts = Note.where(commit_id: @commits.map(&:id)).
       group(:commit_id).count
@@ -246,7 +250,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   end
 
   def define_show_vars
-    @participants = @merge_request.participants(current_user, @project)
+    @participants = @merge_request.participants(current_user)
 
     # Build a note object for comment form
     @note = @project.notes.new(noteable: @merge_request)
@@ -259,7 +263,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     @commits = @merge_request.commits
 
     @merge_request_diff = @merge_request.merge_request_diff
-    
+
     if @merge_request.locked_long_ago?
       @merge_request.unlock_mr
       @merge_request.close
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 86f4a02a6e99ddf98c57ab95be9c02e6d84f53dc..15506bd677ad0200c73cad9127bfdf0b2015ad75 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -75,11 +75,7 @@ class Projects::MilestonesController < Projects::ApplicationController
   end
 
   def sort_issues
-    @issues = @milestone.issues.where(id: params['sortable_issue'])
-    @issues.each do |issue|
-      issue.position = params['sortable_issue'].index(issue.id.to_s) + 1
-      issue.save
-    end
+    @milestone.sort_issues(params['sortable_issue'].map(&:to_i))
 
     render json: { saved: true }
   end
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 0f5d82ce133828bfae5b4b6054f3491be41accc0..41cd08c93c60bfed5fa7ac6f0e14c85055f86162 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -25,7 +25,7 @@ class Projects::NotesController < Projects::ApplicationController
 
     respond_to do |format|
       format.json { render_note_json(@note) }
-      format.html { redirect_to :back }
+      format.html { redirect_back_or_default }
     end
   end
 
@@ -34,7 +34,7 @@ class Projects::NotesController < Projects::ApplicationController
 
     respond_to do |format|
       format.json { render_note_json(@note) }
-      format.html { redirect_to :back }
+      format.html { redirect_back_or_default }
     end
   end
 
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index cf73bc01c8fb56fd5e074461297712b278196f3c..9de5269cd2520abaea1b308e694dfd3999754f9d 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -72,7 +72,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController
 
   def leave
     if @project.namespace == current_user.namespace
-      return redirect_to(:back, alert: 'You can not leave your own project. Transfer or delete the project.')
+      message = 'You can not leave your own project. Transfer or delete the project.'
+      return redirect_back_or_default(default: { action: 'index' }, options: { alert: message })
     end
 
     @project.project_members.find_by(user_id: current_user).destroy
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index 6080c849c8d30b42a8ca0392d8fa65a9626e2438..c4e18c170777c5f01a8ad846723ebcac45c01ca7 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -3,6 +3,7 @@ class Projects::RefsController < Projects::ApplicationController
   include TreeHelper
 
   before_action :require_non_empty_project
+  before_action :validate_ref_id
   before_action :assign_ref_vars
   before_action :authorize_download_code!
 
@@ -71,4 +72,10 @@ class Projects::RefsController < Projects::ApplicationController
       format.js
     end
   end
+
+  private
+
+  def validate_ref_id
+    return not_found! if params[:id].present? && params[:id] !~ Gitlab::Regex.git_reference_regex
+  end
 end
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0825a4311cbb62b5988847114566e7b2f4d64ff9
--- /dev/null
+++ b/app/controllers/projects/releases_controller.rb
@@ -0,0 +1,31 @@
+class Projects::ReleasesController < Projects::ApplicationController
+  # Authorize
+  before_action :require_non_empty_project
+  before_action :authorize_download_code!
+  before_action :authorize_push_code!
+  before_action :tag
+  before_action :release
+
+  def edit
+  end
+
+  def update
+    release.update_attributes(release_params)
+
+    redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name)
+  end
+
+  private
+
+  def tag
+    @tag ||= @repository.find_tag(params[:tag_id])
+  end
+
+  def release
+    @release ||= @project.releases.find_or_initialize_by(tag: @tag.name)
+  end
+
+  def release_params
+    params.require(:release).permit(:description)
+  end
+end
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index c4a5e2d63598f624c6a7990cb64913cd65cc769e..ba9aea1c165335c4f1bb898409794e64963500bf 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -11,18 +11,9 @@ class Projects::RepositoriesController < Projects::ApplicationController
   end
 
   def archive
-    begin
-      file_path = ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute
-    rescue
-      return head :not_found
-    end
-
-    if file_path
-      # Send file to user
-      response.headers["Content-Length"] = File.open(file_path).size.to_s
-      send_file file_path
-    else
-      redirect_to request.fullpath
-    end
+    render json: ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute
+  rescue => ex
+    logger.error("#{self.class.name}: #{ex}")
+    return git_not_found!
   end
 end
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index 6cb6e3ef6d4412b096db084a4da945fcf29a4525..bfbcf2567f3a2f1f693941a9dfcadc916bda26c4 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -6,11 +6,10 @@ class Projects::RunnersController < Projects::ApplicationController
   layout 'project_settings'
 
   def index
-    @runners = @ci_project.runners.order('id DESC')
-    @specific_runners =
-      Ci::Runner.specific.includes(:runner_projects).
-      where(Ci::RunnerProject.table_name => { project_id: current_user.authorized_projects } ).
-      where.not(id: @runners).order("#{Ci::Runner.table_name}.id DESC").page(params[:page]).per(20)
+    @runners = @ci_project.runners.ordered
+    @specific_runners = current_user.ci_authorized_runners.
+      where.not(id: @ci_project.runners).
+      ordered.page(params[:page]).per(20)
     @shared_runners = Ci::Runner.shared.active
     @shared_runners_count = @shared_runners.count(:all)
   end
@@ -60,6 +59,6 @@ class Projects::RunnersController < Projects::ApplicationController
   end
 
   def runner_params
-    params.require(:runner).permit(:description, :tag_list, :contacted_at, :active)
+    params.require(:runner).permit(:description, :tag_list, :active)
   end
 end
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 3047ee8a1ff02db6b068541daa467334b074e436..42dbb497e013ace446d1b38e76c37c9e84b35124 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -9,6 +9,10 @@ class Projects::ServicesController < Projects::ApplicationController
                     :note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url,
                     :notify, :color,
                     :server_host, :server_port, :default_irc_uri, :enable_ssl_verification]
+
+  # Parameters to ignore if no value is specified
+  FILTER_BLANK_PARAMS = [:password]
+
   # Authorize
   before_action :authorize_admin_project!
   before_action :service, only: [:edit, :update, :test]
@@ -48,7 +52,7 @@ class Projects::ServicesController < Projects::ApplicationController
       message = { alert: error_message }
     end
 
-    redirect_to :back, message
+    redirect_back_or_default(options: message)
   end
 
   private
@@ -59,7 +63,9 @@ class Projects::ServicesController < Projects::ApplicationController
 
   def service_params
     service_params = params.require(:service).permit(ALLOWED_PARAMS)
-    service_params.delete("password") if service_params["password"].blank?
+    FILTER_BLANK_PARAMS.each do |param|
+      service_params.delete(param) if service_params[param].blank?
+    end
     service_params
   end
 end
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index b07a2a8db2f7d121a5f8ad50a1c8d1433725d5f3..2104c7a7a718f7f70b07660407b8e7c7865ed7cc 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -21,6 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController
       filter: :by_project,
       project: @project
     })
+    @snippets = @snippets.page(params[:page]).per(PER_PAGE)
   end
 
   def new
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index f565fbbbbc3fd9a2dcf9c52479cebbcd504c9944..cb39c2b878283dba5663642c7da542caab4dcbea 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -8,15 +8,23 @@ class Projects::TagsController < Projects::ApplicationController
   def index
     sorted = VersionSorter.rsort(@repository.tag_names)
     @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE)
+    @releases = project.releases.where(tag: @tags)
+  end
+
+  def show
+    @tag = @repository.find_tag(params[:id])
+    @release = @project.releases.find_or_initialize_by(tag: @tag.name)
+    @commit = @repository.commit(@tag.target)
   end
 
   def create
     result = CreateTagService.new(@project, current_user).
-      execute(params[:tag_name], params[:ref], params[:message])
+      execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
 
     if result[:status] == :success
       @tag = result[:tag]
-      redirect_to namespace_project_tags_path(@project.namespace, @project)
+
+      redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name)
     else
       @error = result[:message]
       render action: 'new'
@@ -26,12 +34,6 @@ class Projects::TagsController < Projects::ApplicationController
   def destroy
     DeleteTagService.new(project, current_user).execute(params[:id])
 
-    respond_to do |format|
-      format.html do
-        redirect_to namespace_project_tags_path(@project.namespace,
-                                                @project)
-      end
-      format.js
-    end
+    redirect_to namespace_project_tags_path(@project.namespace, @project)
   end
 end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 213c2a7173b4517c1080ed80a0e3e632c4133b10..00d13a83ce8372b44a099389564baa534be8a981 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -1,11 +1,14 @@
 class ProjectsController < ApplicationController
-  prepend_before_filter :render_go_import, only: [:show]
+  include ExtractsPath
+
+  prepend_before_action :render_go_import, only: [:show]
   skip_before_action :authenticate_user!, only: [:show, :activity]
   before_action :project, except: [:new, :create]
   before_action :repository, except: [:new, :create]
+  before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
 
   # Authorize
-  before_action :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
+  before_action :authorize_admin_project!, only: [:edit, :update]
   before_action :event_filter, only: [:show, :activity]
 
   layout :determine_layout
@@ -56,6 +59,8 @@ class ProjectsController < ApplicationController
   end
 
   def transfer
+    return access_denied! unless can?(current_user, :change_namespace, @project)
+
     namespace = Namespace.find_by(id: params[:new_namespace_id])
     ::Projects::TransferService.new(project, current_user).execute(namespace)
 
@@ -64,6 +69,15 @@ class ProjectsController < ApplicationController
     end
   end
 
+  def remove_fork
+    return access_denied! unless can?(current_user, :remove_fork_project, @project)
+
+    if @project.forked?
+      @project.forked_project_link.destroy
+      flash[:notice] = 'The fork relationship has been removed.'
+    end
+  end
+
   def activity
     respond_to do |format|
       format.html
@@ -87,7 +101,7 @@ class ProjectsController < ApplicationController
             render 'projects/empty'
           else
             if current_user
-              @membership = @project.project_member_by_id(current_user.id)
+              @membership = @project.team.find_member(current_user.id)
             end
 
             render :show
@@ -110,11 +124,7 @@ class ProjectsController < ApplicationController
     ::Projects::DestroyService.new(@project, current_user, {}).execute
     flash[:alert] = "Project '#{@project.name}' was deleted."
 
-    if request.referer.include?('/admin')
-      redirect_to admin_namespaces_projects_path
-    else
-      redirect_to dashboard_projects_path
-    end
+    redirect_back_or_default(default: dashboard_projects_path, options: {})
   rescue Projects::DestroyService::DestroyError => ex
     redirect_to edit_project_path(@project), alert: ex.message
   end
@@ -139,6 +149,7 @@ class ProjectsController < ApplicationController
 
   def archive
     return access_denied! unless can?(current_user, :archive_project, @project)
+
     @project.archive!
 
     respond_to do |format|
@@ -148,6 +159,7 @@ class ProjectsController < ApplicationController
 
   def unarchive
     return access_denied! unless can?(current_user, :archive_project, @project)
+
     @project.unarchive!
 
     respond_to do |format|
@@ -225,4 +237,14 @@ class ProjectsController < ApplicationController
 
     render "go_import", layout: false
   end
+
+  def repo_exists?
+    project.repository_exists? && !project.empty_repo?
+  end
+
+  # Override get_id from ExtractsPath, which returns the branch and file path 
+  # for the blob/tree, which in this case is just the root of the default branch.
+  def get_id
+    project.repository.root_ref
+  end
 end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index eb0408a95e58c3b64349ebdcf8d2633b0303082f..9bb42ec86b390b88a70d0494ed308f73e744118a 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -23,8 +23,8 @@ class SearchController < ApplicationController
 
     @search_results =
       if @project
-        unless %w(blobs notes issues merge_requests milestones wiki_blobs).
-          include?(@scope)
+        unless %w(blobs notes issues merge_requests milestones wiki_blobs
+                  commits).include?(@scope)
           @scope = 'blobs'
         end
 
diff --git a/app/controllers/sherlock/application_controller.rb b/app/controllers/sherlock/application_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..682ca5e382155f51ccc84432c29b9ecb13ea296a
--- /dev/null
+++ b/app/controllers/sherlock/application_controller.rb
@@ -0,0 +1,12 @@
+module Sherlock
+  class ApplicationController < ::ApplicationController
+    before_action :find_transaction
+
+    def find_transaction
+      if params[:transaction_id]
+        @transaction = Gitlab::Sherlock.collection.
+          find_transaction(params[:transaction_id])
+      end
+    end
+  end
+end
diff --git a/app/controllers/sherlock/file_samples_controller.rb b/app/controllers/sherlock/file_samples_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0c3bc100106049688ed7a7b0155b6de1a4e4e779
--- /dev/null
+++ b/app/controllers/sherlock/file_samples_controller.rb
@@ -0,0 +1,7 @@
+module Sherlock
+  class FileSamplesController < Sherlock::ApplicationController
+    def show
+      @file_sample = @transaction.find_file_sample(params[:id])
+    end
+  end
+end
diff --git a/app/controllers/sherlock/queries_controller.rb b/app/controllers/sherlock/queries_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..63b26aab1a4b96475e8ec294585bad0c2ad16867
--- /dev/null
+++ b/app/controllers/sherlock/queries_controller.rb
@@ -0,0 +1,7 @@
+module Sherlock
+  class QueriesController < Sherlock::ApplicationController
+    def show
+      @query = @transaction.find_query(params[:id])
+    end
+  end
+end
diff --git a/app/controllers/sherlock/transactions_controller.rb b/app/controllers/sherlock/transactions_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ccc739da879a1d3f9baaa1341622847fac6c63d4
--- /dev/null
+++ b/app/controllers/sherlock/transactions_controller.rb
@@ -0,0 +1,19 @@
+module Sherlock
+  class TransactionsController < Sherlock::ApplicationController
+    def index
+      @transactions = Gitlab::Sherlock.collection.newest_first
+    end
+
+    def show
+      @transaction = Gitlab::Sherlock.collection.find_transaction(params[:id])
+
+      render_404 unless @transaction
+    end
+
+    def destroy_all
+      Gitlab::Sherlock.collection.clear
+
+      redirect_to(:back)
+    end
+  end
+end
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index d3597ef090192ee01f5380c84344df1303ed7b79..b5f3176461c5a2894ae0f3ceb9e2b1512d667d05 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -6,33 +6,34 @@ class GroupsFinder
   private
 
   def all_groups(current_user)
-    if current_user
-      if current_user.authorized_groups.any?
-        # User has access to groups
-        #
-        # Return only:
-        #   groups with public projects
-        #   groups with internal projects
-        #   groups with joined projects
-        #
-        group_ids = Project.public_and_internal_only.pluck(:namespace_id) +
-          current_user.authorized_groups.pluck(:id)
-        Group.where(id: group_ids)
-      else
-        # User has no group membership
-        #
-        # Return only:
-        #   groups with public projects
-        #   groups with internal projects
-        #
-        Group.where(id: Project.public_and_internal_only.pluck(:namespace_id))
-      end
-    else
-      # Not authenticated
-      #
-      # Return only:
-      #   groups with public projects
-      Group.where(id: Project.public_only.pluck(:namespace_id))
-    end
+    group_ids = if current_user
+                  if current_user.authorized_groups.any?
+                    # User has access to groups
+                    #
+                    # Return only:
+                    #   groups with public projects
+                    #   groups with internal projects
+                    #   groups with joined projects
+                    #
+                    Project.public_and_internal_only.pluck(:namespace_id) +
+                      current_user.authorized_groups.pluck(:id)
+                  else
+                    # User has no group membership
+                    #
+                    # Return only:
+                    #   groups with public projects
+                    #   groups with internal projects
+                    #
+                    Project.public_and_internal_only.pluck(:namespace_id)
+                  end
+                else
+                  # Not authenticated
+                  #
+                  # Return only:
+                  #   groups with public projects
+                  Project.public_only.pluck(:namespace_id)
+                end
+
+    Group.where("public IS TRUE OR id IN(?)", group_ids)
   end
 end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 97c7e74c2944aaa2a2285bdd503a5f65d195fbe1..c407dfc163a9bdd1ed50c98b033b44ba65106c4c 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -53,15 +53,36 @@ class IssuableFinder
       end
   end
 
+  def project?
+    params[:project_id].present?
+  end
+
   def project
     return @project if defined?(@project)
 
-    @project =
-      if params[:project_id].present?
-        Project.find(params[:project_id])
-      else
-        nil
-      end
+    if project?
+      @project = Project.find(params[:project_id])
+      
+      unless Ability.abilities.allowed?(current_user, :read_project, @project)
+        @project = nil
+      end 
+    else
+      @project = nil
+    end
+
+    @project
+  end
+
+  def projects
+    return @projects if defined?(@projects)
+
+    if project?
+      project
+    elsif current_user && params[:authorized_only].presence && !current_user_related?
+      current_user.authorized_projects
+    else
+      ProjectsFinder.new.execute(current_user)
+    end
   end
 
   def search
@@ -72,7 +93,7 @@ class IssuableFinder
     params[:milestone_title].present?
   end
 
-  def no_milestones?
+  def filter_by_no_milestone?
     milestones? && params[:milestone_title] == Milestone::None.title
   end
 
@@ -81,12 +102,22 @@ class IssuableFinder
 
     @milestones =
       if milestones?
-        Milestone.where(title: params[:milestone_title])
+        scope = Milestone.where(project_id: projects)
+
+        scope.where(title: params[:milestone_title])
       else
         nil
       end
   end
 
+  def labels?
+    params[:label_name].present?
+  end
+
+  def filter_by_no_label?
+    labels? && params[:label_name] == Label::None.title
+  end
+
   def assignee?
     params[:assignee_id].present?
   end
@@ -120,19 +151,7 @@ class IssuableFinder
   private
 
   def init_collection
-    table_name = klass.table_name
-
-    if project
-      if Ability.abilities.allowed?(current_user, :read_project, project)
-        project.send(table_name)
-      else
-        []
-      end
-    elsif current_user && params[:authorized_only].presence && !current_user_related?
-      klass.of_projects(current_user.authorized_projects).references(:project)
-    else
-      klass.of_projects(ProjectsFinder.new.execute(current_user)).references(:project)
-    end
+    klass.all
   end
 
   def by_scope(items)
@@ -170,7 +189,12 @@ class IssuableFinder
   end
 
   def by_project(items)
-    items = items.of_projects(project.id) if project
+    items =
+      if projects
+        items.of_projects(projects).references(:project)
+      else
+        items.none
+      end
 
     items
   end
@@ -185,18 +209,6 @@ class IssuableFinder
     items.sort(params[:sort])
   end
 
-  def by_milestone(items)
-    if milestones?
-      if no_milestones?
-        items = items.where(milestone_id: [-1, nil])
-      else
-        items = items.where(milestone_id: milestones.try(:pluck, :id))
-      end
-    end
-
-    items
-  end
-
   def by_assignee(items)
     if assignee?
       items = items.where(assignee_id: assignee.try(:id))
@@ -213,20 +225,36 @@ class IssuableFinder
     items
   end
 
-  def by_label(items)
-    if params[:label_name].present?
-      if params[:label_name] == Label::None.title
-        item_ids = LabelLink.where(target_type: klass.name).pluck(:target_id)
+  def by_milestone(items)
+    if milestones?
+      if filter_by_no_milestone?
+        items = items.where(milestone_id: [-1, nil])
+      else
+        items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] })
+
+        if projects
+          items = items.where(milestones: { project_id: projects })
+        end
+      end
+    end
+
+    items
+  end
 
-        items = items.where('id NOT IN (?)', item_ids)
+  def by_label(items)
+    if labels?
+      if filter_by_no_label?
+        items = items.
+          joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{klass.name}' AND label_links.target_id = #{klass.table_name}.id").
+          where(label_links: { id: nil })
       else
         label_names = params[:label_name].split(",")
 
-        item_ids = LabelLink.joins(:label).
-          where('labels.title in (?)', label_names).
-          where(target_type: klass.name).pluck(:target_id)
+        items = items.joins(:labels).where(labels: { title: label_names })
 
-        items = items.where(id: item_ids)
+        if projects
+          items = items.where(labels: { project_id: projects })
+        end
       end
     end
 
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index 14df8d4cbd7323dfe54997e0490be24aa0ace65d..c5820bf4c5026137f02662b8982f5b30934d7e08 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -16,6 +16,6 @@ module AppearancesHelper
   end
 
   def brand_header_logo
-    image_tag 'logo.svg'
+    render 'shared/logo.svg'
   end
 end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index cab2278adb7f8fe3c072d378021566082f9c1d42..8ecdeaf8e76ba8fe241838106c6ed9bc9509579c 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -68,13 +68,17 @@ module ApplicationHelper
     end
   end
 
-  def avatar_icon(user_email = '', size = nil)
-    user = User.find_by(email: user_email)
+  def avatar_icon(user_or_email = nil, size = nil)
+    if user_or_email.is_a?(User)
+      user = user_or_email
+    else
+      user = User.find_by(email: user_or_email)
+    end
 
     if user
       user.avatar_url(size) || default_avatar
     else
-      gravatar_icon(user_email, size)
+      gravatar_icon(user_or_email, size)
     end
   end
 
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index cd99a2324038d4ddb5e277927a6be5589817b3ae..2c81ea1623c52f9945519e121e0df1f839926ec1 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -1,5 +1,5 @@
 module AuthHelper
-  PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze
+  PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook).freeze
   FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze
 
   def ldap_enabled?
diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb
deleted file mode 100644
index 1b5a2c31d74660e4fb01931bf5b4aa0135affd23..0000000000000000000000000000000000000000
--- a/app/helpers/builds_helper.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module BuildsHelper
-  def build_ref_link build
-    gitlab_ref_link build.project, build.ref
-  end
-
-  def build_commit_link build
-    gitlab_commit_link build.project, build.short_sha
-  end
-
-  def build_url(build)
-    namespace_project_build_path(build.gl_project, build.project, build)
-  end
-end
diff --git a/app/helpers/ci/gitlab_helper.rb b/app/helpers/ci/gitlab_helper.rb
index baddbc806f2d386d31ec6e85605ba7af0136a2c4..e34c8be1dfc5249f7cd40f9023358fb63328806b 100644
--- a/app/helpers/ci/gitlab_helper.rb
+++ b/app/helpers/ci/gitlab_helper.rb
@@ -4,25 +4,6 @@ module Ci
       { :"data-no-turbolink" => "data-no-turbolink" }
     end
 
-    def gitlab_ref_link project, ref
-      gitlab_url = project.gitlab_url.dup
-      gitlab_url << "/commits/#{ref}"
-      link_to ref, gitlab_url, no_turbolink
-    end
-
-    def gitlab_compare_link project, before, after
-      gitlab_url = project.gitlab_url.dup
-      gitlab_url << "/compare/#{before}...#{after}"
-
-      link_to "#{before}...#{after}", gitlab_url, no_turbolink
-    end
-
-    def gitlab_commit_link project, sha
-      gitlab_url = project.gitlab_url.dup
-      gitlab_url << "/commit/#{sha}"
-      link_to Ci::Commit.truncate_sha(sha), gitlab_url, no_turbolink
-    end
-
     def yaml_web_editor_link(project)
       commits = project.commits
 
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index dbd1e26fa79b924fdb7595ed004cf5aadc243d6b..0ecf77bb45ee99052bb664d631f19bdffa462c83 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -1,7 +1,7 @@
 module CiStatusHelper
   def ci_status_path(ci_commit)
     project = ci_commit.gl_project
-    ci_namespace_project_commit_path(project.namespace, project, ci_commit.sha)
+    builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha)
   end
 
   def ci_status_icon(ci_commit)
@@ -42,4 +42,13 @@ module CiStatusHelper
 
     icon(icon_name)
   end
+
+  def render_ci_status(ci_commit)
+    link_to ci_status_path(ci_commit),
+      class: "c#{ci_status_color(ci_commit)}",
+      title: "Build status: #{ci_commit.status}",
+      data: { toggle: 'tooltip', placement: 'left' } do
+      ci_status_icon(ci_commit)
+    end
+  end
 end
diff --git a/app/helpers/clipboard_helper.rb b/app/helpers/clipboard_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3c1d7569fac3df89c38b464502382bd4763e521b
--- /dev/null
+++ b/app/helpers/clipboard_helper.rb
@@ -0,0 +1,8 @@
+module ClipboardHelper
+  def clipboard_button
+    content_tag :button,
+      icon('clipboard'),
+      class: 'btn btn-xs btn-clipboard js-clipboard-trigger',
+      type: :button
+  end
+end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index b896fba3704ed7fcc38eaec39dfc1de49eda9275..b889fb2897349f20ec7b80cce26843495f08d252 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -137,7 +137,7 @@ module DiffHelper
     # Always use HTML to handle case where JSON diff rendered this button
     params_copy.delete(:format)
 
-    link_to url_for(params_copy), id: "inline-diff-btn", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do
+    link_to url_for(params_copy), id: "inline-diff-btn", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do
       'Inline'
     end
   end
@@ -148,7 +148,7 @@ module DiffHelper
     # Always use HTML to handle case where JSON diff rendered this button
     params_copy.delete(:format)
 
-    link_to url_for(params_copy), id: "parallel-diff-btn", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do
+    link_to url_for(params_copy), id: "parallel-diff-btn", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do
       'Side-by-side'
     end
   end
@@ -170,7 +170,8 @@ module DiffHelper
 
   def commit_for_diff(diff)
     if diff.deleted_file
-      @merge_request ? @merge_request.commits.last : @commit.parents.first
+      first_commit = @first_commit || @commit
+      first_commit.parent
     else
       @commit
     end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 12b87dca79820e2dd72b2580bed09d679faa15bf..658134821202f227b264514bbb58161128949021 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -19,7 +19,8 @@ module GitlabMarkdownHelper
                      escape_once(body)
                    end
 
-    gfm_body = Gitlab::Markdown.gfm(escaped_body, project: @project, current_user: current_user)
+    user = current_user if defined?(current_user)
+    gfm_body = Gitlab::Markdown.gfm(escaped_body, project: @project, current_user: user)
 
     fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body)
     if fragment.children.size == 1 && fragment.children[0].name == 'a'
@@ -45,29 +46,39 @@ module GitlabMarkdownHelper
   end
 
   def markdown(text, context = {})
+    return "" unless text.present?
+
     context.reverse_merge!(
-      current_user: current_user,
       path:         @path,
+      pipeline:     :default,
       project:      @project,
       project_wiki: @project_wiki,
       ref:          @ref
     )
 
-    Gitlab::Markdown.render(text, context)
+    user = current_user if defined?(current_user)
+
+    html = Gitlab::Markdown.render(text, context)
+    Gitlab::Markdown.post_process(html, pipeline: context[:pipeline], project: @project, user: user)
   end
 
   # TODO (rspeicher): Remove all usages of this helper and just call `markdown`
   # with a custom pipeline depending on the content being rendered
   def gfm(text, options = {})
+    return "" unless text.present?
+
     options.reverse_merge!(
-      current_user: current_user,
       path:         @path,
+      pipeline:     :default,
       project:      @project,
       project_wiki: @project_wiki,
       ref:          @ref
     )
 
-    Gitlab::Markdown.gfm(text, options)
+    user = current_user if defined?(current_user)
+
+    html = Gitlab::Markdown.gfm(text, options)
+    Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], project: @project, user: user)
   end
 
   def asciidoc(text)
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index 4d9da6ff837bb2ef86b77b926d8886b67e23967f..b0b536d4649aef959d25a6e9e8e5d330eb18e110 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -25,6 +25,10 @@ module GitlabRoutingHelper
     namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref)
   end
 
+  def project_builds_path(project, *args)
+    namespace_project_builds_path(project.namespace, project, *args)
+  end
+
   def activity_project_path(project, *args)
     activity_namespace_project_path(project.namespace, project, *args)
   end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 6ddb37cd0dc9e7f4c343fd771210cb123b3cf9c6..fda18e7b316d7c130338ac6e85a47eaf0d17d3bc 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -83,6 +83,10 @@ module IssuesHelper
     end
   end
 
+  def merge_requests_sentence(merge_requests)
+    merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ')
+  end
+
   # Required for Gitlab::Markdown::IssueReferenceFilter
   module_function :url_for_issue
 end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 66b18eea69990a6c90588d5e315361ecbd459e41..ee04ace35d0933e74386739f275c625d4a6ecedb 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -92,11 +92,19 @@ module LabelsHelper
     end
   end
 
-  def project_labels_options(project)
-    labels = project.labels.to_a
-    labels.unshift(Label::None)
-    labels.unshift(Label::Any)
-    options_from_collection_for_select(labels, 'name', 'title', params[:label_name])
+  def projects_labels_options
+    labels =
+      if @project
+        @project.labels
+      else
+        Label.where(project_id: @projects)
+      end
+
+    grouped_labels = Labels::GroupService.new(labels).execute
+    grouped_labels.unshift(Label::None)
+    grouped_labels.unshift(Label::Any)
+
+    options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name])
   end
 
   # Required for Gitlab::Markdown::LabelReferenceFilter
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 81773e7afcf77380edcb960925c8557b093f55dd..728d877ace226e73b574c191fec62875a3f22415 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -47,7 +47,7 @@ module MergeRequestsHelper
   end
 
   def issues_sentence(issues)
-    issues.map { |i| "##{i.iid}" }.to_sentence
+    issues.map(&:to_reference).to_sentence
   end
 
   def mr_change_branches_path(merge_request)
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index 4710171ebaaf2b4ece5e7bc151a27f05164619d3..c73cb3028eebe65c980c8e7dd9225b2db6cdb20c 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -34,7 +34,8 @@ module PreferencesHelper
   def project_view_choices
     [
       ['Readme (default)', :readme],
-      ['Activity view', :activity]
+      ['Activity view', :activity],
+      ['Files view', :files]
     ]
   end
 
@@ -46,8 +47,7 @@ module PreferencesHelper
     Gitlab::ColorSchemes.for_user(current_user).css_class
   end
 
-  def prefer_readme?
-    !current_user ||
-      current_user.project_view == 'readme'
+  def default_project_view
+    current_user ? current_user.project_view : 'readme'
   end
 end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index a0220af4c30c75cab155583eac8ee7fe8c983b26..5301c2ccf7688a0b293f28fe27b3577e23cde479 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -29,7 +29,7 @@ module ProjectsHelper
     author_html =  ""
 
     # Build avatar image tag
-    author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
+    author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
 
     # Build name span tag
     author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name]
@@ -70,6 +70,10 @@ module ProjectsHelper
     "You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
   end
 
+  def remove_fork_project_message(project)
+    "You are going to remove the fork relationship to source project #{@project.forked_from_project.name_with_namespace}.  Are you ABSOLUTELY sure?"
+  end
+
   def project_nav_tabs
     @nav_tabs ||= get_project_nav_tabs(@project, current_user)
   end
@@ -113,6 +117,10 @@ module ProjectsHelper
       nav_tabs << :merge_requests
     end
 
+    if project.gitlab_ci? && can?(current_user, :read_build, project)
+      nav_tabs << :builds
+    end
+
     if can?(current_user, :admin_project, project)
       nav_tabs << :settings
     end
diff --git a/app/helpers/runners_helper.rb b/app/helpers/runners_helper.rb
index 5d7d06c84903db173ea00ad3945f1f4a758d5b54..46eb82a354ff543ff0e8179f3c2bd561a4fa2424 100644
--- a/app/helpers/runners_helper.rb
+++ b/app/helpers/runners_helper.rb
@@ -1,20 +1,29 @@
 module RunnersHelper
   def runner_status_icon(runner)
-    unless runner.contacted_at
-      return content_tag :i, nil,
-        class: "fa fa-warning-sign",
-        title: "New runner. Has not connected yet"
+    status = runner.status
+    case status
+    when :not_connected
+      content_tag :i, nil,
+                  class: "fa fa-warning",
+                  title: "New runner. Has not connected yet"
+
+    when :online, :offline, :paused
+      content_tag :i, nil,
+                  class: "fa fa-circle runner-status-#{status}",
+                  title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
     end
+  end
 
-    status =
-      if runner.active?
-        runner.contacted_at > 3.hour.ago ? :online : :offline
-      else
-        :paused
-      end
+  def runner_link(runner)
+    display_name = truncate(runner.display_name, length: 15)
+    id = "\##{runner.id}"
 
-    content_tag :i, nil,
-      class: "fa fa-circle runner-status-#{status}",
-      title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago"
+    if current_user && current_user.admin
+      link_to ci_admin_runner_path(runner) do
+        display_name + id
+      end
+    else
+      display_name + id
+    end
   end
 end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index c31a556ff7b3d83a0ab6663713eb0d21b98b3893..a6ee6880247199e2f0b8e3a8b7a3cf286513e927 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -70,7 +70,7 @@ module SearchHelper
 
   # Autocomplete results for the current user's groups
   def groups_autocomplete(term, limit = 5)
-    current_user.authorized_groups.search(term).limit(limit).map do |group|
+    GroupsFinder.new.execute(current_user).search(term).limit(limit).map do |group|
       {
         label: "group: #{search_result_sanitize(group.name)}",
         url: group_path(group)
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index 0e7d8065ac799003201fbf78cd125ba38c74b383..04e53fe7c6100baabf913968a5d2bc5192a6ffbe 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -110,22 +110,4 @@ module TabHelper
       'active'
     end
   end
-
-  # Use nav_tab for save controller/action  but different params
-  def nav_tab(key, value, &block)
-    o = {}
-    o[:class] = ""
-
-    if value.nil?
-      o[:class] << " active" if params[key].blank?
-    else
-      o[:class] << " active" if params[key] == value
-    end
-
-    if block_given?
-      content_tag(:li, capture(&block), o)
-    else
-      content_tag(:li, nil, o)
-    end
-  end
 end
diff --git a/app/mailers/abuse_report_mailer.rb b/app/mailers/abuse_report_mailer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f0c41f69a5c5b15eefcd7e107f6faf7fc989a37a
--- /dev/null
+++ b/app/mailers/abuse_report_mailer.rb
@@ -0,0 +1,12 @@
+class AbuseReportMailer < BaseMailer
+  include Gitlab::CurrentSettings
+
+  def notify(abuse_report_id)
+    @abuse_report = AbuseReport.find(abuse_report_id)
+
+    mail(
+      to:       current_application_settings.admin_notification_email, 
+      subject:  "#{@abuse_report.user.name} (#{@abuse_report.user.username}) was reported for abuse"
+    )
+  end
+end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 77c121ca5e851bd40b7f10956ad0dda5fa06b80d..b72178fa12608f84058cb356ee71d7c798e8ebc4 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -41,6 +41,7 @@ class Ability
           :read_project_member,
           :read_merge_request,
           :read_note,
+          :read_build,
           :download_code
         ]
 
@@ -127,6 +128,7 @@ class Ability
         :read_project_member,
         :read_merge_request,
         :read_note,
+        :read_build,
         :create_project,
         :create_issue,
         :create_note
@@ -187,7 +189,8 @@ class Ability
         :change_visibility_level,
         :rename_project,
         :remove_project,
-        :archive_project
+        :archive_project,
+        :remove_fork_project
       ]
     end
 
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index c8841178e933079568b33e330775dce0ce21fd27..266045f7afaaff8ddb47bdc34c71027b360e7d68 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -44,6 +44,10 @@ class ApplicationSetting < ActiveRecord::Base
     allow_blank: true,
     format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }
 
+  validates :admin_notification_email,
+    allow_blank: true,
+    email: true
+
   validates_each :restricted_visibility_levels do |record, attr, value|
     unless value.nil?
       value.each do |level|
@@ -83,7 +87,8 @@ class ApplicationSetting < ActiveRecord::Base
       default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
       default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
       restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
-      import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git']
+      import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
+      shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
     )
   end
 
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index cfafbf6786e025b3862358dfdc2d976a67ac2786..7f185ae7cc34538a79958529096bb47af297ce11 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -93,10 +93,7 @@ module Ci
           Ci::WebHookService.new.build_end(build)
         end
 
-        if build.commit.should_create_next_builds?(build)
-          build.commit.create_next_builds(build.ref, build.tag, build.user, build.trigger_request)
-        end
-
+        build.commit.create_next_builds(build)
         project.execute_services(build)
 
         if project.coverage_enabled?
@@ -109,6 +106,14 @@ module Ci
       failed? && allow_failure?
     end
 
+    def retryable?
+      commands.present?
+    end
+
+    def retried?
+      !self.commit.latest_builds_for_ref(self.ref).include?(self)
+    end
+
     def trace_html
       html = Ci::Ansi2html::convert(trace) if trace.present?
       html || ''
@@ -119,7 +124,7 @@ module Ci
     end
 
     def variables
-      yaml_variables + project_variables + trigger_variables
+      predefined_variables + yaml_variables + project_variables + trigger_variables
     end
 
     def project
@@ -225,12 +230,24 @@ module Ci
     end
 
     def retry_url
-      if commands.present?
+      if retryable?
         Gitlab::Application.routes.url_helpers.
           retry_namespace_project_build_path(gl_project.namespace, gl_project, self)
       end
     end
 
+    def can_be_served?(runner)
+      (tag_list - runner.tag_list).empty?
+    end
+
+    def any_runners_online?
+      project.any_runners? { |runner| runner.active? && runner.online? && can_be_served?(runner) }
+    end
+
+    def show_warning?
+      pending? && !any_runners_online?
+    end
+
     private
 
     def yaml_variables
@@ -258,5 +275,14 @@ module Ci
         []
       end
     end
+
+    def predefined_variables
+      variables = []
+      variables << { key: :CI_BUILD_TAG, value: ref, public: true } if tag?
+      variables << { key: :CI_BUILD_NAME, value: name, public: true }
+      variables << { key: :CI_BUILD_STAGE, value: stage, public: true }
+      variables << { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } if trigger_request
+      variables
+    end
   end
 end
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index 68864edfbbf40155b140a4ac64b22ca7dd29278e..e58420d82d462268fc3ffa3a92659bc58766ecac 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -24,6 +24,8 @@ module Ci
     has_many :builds, class_name: 'Ci::Build'
     has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
 
+    scope :ordered, -> { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }
+
     validates_presence_of :sha
     validate :valid_commit_sha
 
@@ -89,19 +91,28 @@ module Ci
     def create_builds(ref, tag, user, trigger_request = nil)
       return unless config_processor
       config_processor.stages.any? do |stage|
-        CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
+        CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request, 'success').present?
       end
     end
 
-    def create_next_builds(ref, tag, user, trigger_request)
+    def create_next_builds(build)
       return unless config_processor
 
-      stages = builds.where(ref: ref, tag: tag, trigger_request: trigger_request).group_by(&:stage)
+      # don't create other builds if this one is retried
+      latest_builds = builds.similar(build).latest
+      return unless latest_builds.exists?(build.id)
 
-      config_processor.stages.any? do |stage|
-        unless stages.include?(stage)
-          CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
-        end
+      # get list of stages after this build
+      next_stages = config_processor.stages.drop_while { |stage| stage != build.stage }
+      next_stages.delete(build.stage)
+
+      # get status for all prior builds
+      prior_builds = latest_builds.reject { |other_build| next_stages.include?(other_build.stage) }
+      status = Ci::Status.get_status(prior_builds)
+
+      # create builds for next stages based
+      next_stages.any? do |stage|
+        CreateBuildsService.new.execute(self, stage, build.ref, build.tag, build.user, build.trigger_request, status).present?
       end
     end
 
@@ -130,24 +141,7 @@ module Ci
         return 'failed'
       end
 
-      @status ||= begin
-        latest = latest_statuses
-        latest.reject! { |status| status.try(&:allow_failure?) }
-
-        if latest.none?
-          'skipped'
-        elsif latest.all?(&:success?)
-          'success'
-        elsif latest.all?(&:pending?)
-          'pending'
-        elsif latest.any?(&:running?) || latest.any?(&:pending?)
-          'running'
-        elsif latest.all?(&:canceled?)
-          'canceled'
-        else
-          'failed'
-        end
-      end
+      @status ||= Ci::Status.get_status(latest_statuses)
     end
 
     def pending?
@@ -193,7 +187,7 @@ module Ci
     end
 
     def config_processor
-      @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file)
+      @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace)
     rescue Ci::GitlabCiYamlProcessor::ValidationError => e
       save_yaml_error(e.message)
       nil
@@ -217,16 +211,6 @@ module Ci
       update!(committed_at: DateTime.now)
     end
 
-    def should_create_next_builds?(build)
-      # don't create other builds if this one is retried
-      other_builds = builds.similar(build).latest
-      return false unless other_builds.include?(build)
-
-      other_builds.all? do |build|
-        build.success? || build.ignored?
-      end
-    end
-
     private
 
     def save_yaml_error(error)
diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb
index 88ba933a434c9ab55e69614d76d38d0e5b1ab9a1..4e806ca1a68eb5943739a403185841291fc4e231 100644
--- a/app/models/ci/project.rb
+++ b/app/models/ci/project.rb
@@ -99,6 +99,7 @@ module Ci
       def ordered_by_last_commit_date
         last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)"
         joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id").
+          joins(:gl_project).
           order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC")
       end
     end
@@ -115,12 +116,12 @@ module Ci
       web_url
     end
 
-    def any_runners?
-      if runners.active.any?
+    def any_runners?(&block)
+      if runners.active.any?(&block)
         return true
       end
 
-      shared_runners_enabled && Ci::Runner.shared.active.any?
+      shared_runners_enabled && Ci::Runner.shared.active.any?(&block)
     end
 
     def set_default_values
@@ -205,7 +206,7 @@ module Ci
     end
 
     def commits
-      gl_project.ci_commits
+      gl_project.ci_commits.ordered
     end
 
     def builds
diff --git a/app/models/ci/project_status.rb b/app/models/ci/project_status.rb
index b66f1212f239da967a16480ac2fe9b5d428edbc9..2d35aeac2256f44d58b6e612fd6d75a04897d242 100644
--- a/app/models/ci/project_status.rb
+++ b/app/models/ci/project_status.rb
@@ -27,9 +27,5 @@ module Ci
     def human_status
       status
     end
-
-    def last_commit_for_ref(ref)
-      commits.where(ref: ref).last
-    end
   end
 end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 6838ccfaaab4330e58dced4b462bc249b705f6c4..b719ad3c87e987f7a5c828a607931b1edd9efe68 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -20,6 +20,8 @@
 module Ci
   class Runner < ActiveRecord::Base
     extend Ci::Model
+
+    LAST_CONTACT_TIME = 5.minutes.ago
     
     has_many :builds, class_name: 'Ci::Build'
     has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
@@ -33,6 +35,8 @@ module Ci
     scope :shared, ->() { where(is_shared: true) }
     scope :active, ->() { where(active: true) }
     scope :paused, ->() { where(active: false) }
+    scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
+    scope :ordered, ->() { order(id: :desc) }
 
     acts_as_taggable
 
@@ -56,7 +60,7 @@ module Ci
     end
 
     def display_name
-      return token unless !description.blank?
+      return short_sha unless !description.blank?
 
       description
     end
@@ -65,6 +69,20 @@ module Ci
       is_shared
     end
 
+    def online?
+      contacted_at && contacted_at > LAST_CONTACT_TIME
+    end
+
+    def status
+      if contacted_at.nil?
+        :not_connected
+      elsif active?
+        online? ? :online : :offline
+      else
+        :paused
+      end
+    end
+
     def belongs_to_one_project?
       runner_projects.count == 1
     end
@@ -78,7 +96,7 @@ module Ci
     end
 
     def short_sha
-      token[0...10]
+      token[0...8] if token
     end
   end
 end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index d5c50013525a8f17575bf6ae80557df1a0bf5f17..492f6be1ce39be2995c71c57ec2dfa0f2ce44c98 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -2,13 +2,13 @@ class Commit
   extend ActiveModel::Naming
 
   include ActiveModel::Conversion
-  include Mentionable
   include Participable
+  include Mentionable
   include Referable
   include StaticModel
 
   attr_mentionable :safe_message
-  participant :author, :committer, :notes, :mentioned_users
+  participant :author, :committer, :notes
 
   attr_accessor :project
 
@@ -164,6 +164,14 @@ class Commit
     @committer ||= User.find_by_any_email(committer_email)
   end
 
+  def parents
+    @parents ||= parent_ids.map { |id| project.commit(id) }
+  end
+
+  def parent
+    @parent ||= project.commit(self.parent_id) if self.parent_id
+  end
+
   def notes
     project.notes.for_commit_id(self.id)
   end
@@ -181,10 +189,6 @@ class Commit
     @raw.short_id(7)
   end
 
-  def parents
-    @parents ||= Commit.decorate(super, project)
-  end
-
   def ci_commit
     project.ci_commit(sha)
   end
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index b4d91b1b0c321b7c16793d32d20c7f4c24a2a3c1..7d54d83974ada6b9f38a2f368385183796155d85 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -15,11 +15,11 @@ class CommitStatus < ActiveRecord::Base
   scope :pending, -> { where(status: 'pending') }
   scope :success, -> { where(status: 'success') }
   scope :failed, -> { where(status: 'failed')  }
-  scope :running_or_pending, -> { where(status:[:running, :pending]) }
+  scope :running_or_pending, -> { where(status: [:running, :pending]) }
+  scope :finished, -> { where(status: [:success, :failed, :canceled]) }
   scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) }
   scope :ordered, -> { order(:ref, :stage_idx, :name) }
   scope :for_ref, ->(ref) { where(ref: ref) }
-  scope :running_or_pending, -> { where(status: [:running, :pending]) }
 
   state_machine :status, initial: :pending do
     event :run do
@@ -27,7 +27,7 @@ class CommitStatus < ActiveRecord::Base
     end
 
     event :drop do
-      transition running: :failed
+      transition [:pending, :running] => :failed
     end
 
     event :success do
@@ -88,4 +88,8 @@ class CommitStatus < ActiveRecord::Base
   def retry_url
     nil
   end
+
+  def show_warning?
+    false
+  end
 end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 4db4ffb2e79835fb234e53470f32da2c105fad6a..5e964f04ef55a39c45212bbbaa01f9ecd575475a 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -6,8 +6,8 @@
 #
 module Issuable
   extend ActiveSupport::Concern
-  include Mentionable
   include Participable
+  include Mentionable
 
   included do
     belongs_to :author, class_name: "User"
@@ -47,7 +47,7 @@ module Issuable
              prefix: true
 
     attr_mentionable :title, :description
-    participant :author, :assignee, :notes, :mentioned_users
+    participant :author, :assignee, :notes_with_associations
   end
 
   module ClassMethods
@@ -85,6 +85,10 @@ module Issuable
     assignee_id_changed?
   end
 
+  def open?
+    opened? || reopened?
+  end
+
   #
   # Votes
   #
@@ -176,6 +180,10 @@ module Issuable
     self.class.to_s.underscore
   end
 
+  def notes_with_associations
+    notes.includes(:author, :project)
+  end
+
   private
 
   def filter_superceded_votes(votes, notes)
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 5b0ae41164200ed37a0eedfb994cacecdbcd4378..193c91f1742940f4c87c1149ca2dec0d325a1c86 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -20,6 +20,12 @@ module Mentionable
     end
   end
 
+  included do
+    if self < Participable
+      participant ->(current_user) { mentioned_users(current_user, load_lazy_references: false) }
+    end
+  end
+
   # Returns the text used as the body of a Note when this object is referenced
   #
   # By default this will be the class name and the result of calling
@@ -41,55 +47,49 @@ module Mentionable
     self
   end
 
-  # Determine whether or not a cross-reference Note has already been created between this Mentionable and
-  # the specified target.
-  def has_mentioned?(target)
-    SystemNoteService.cross_reference_exists?(target, local_reference)
+  def all_references(current_user = self.author, text = self.mentionable_text, load_lazy_references: true)
+    ext = Gitlab::ReferenceExtractor.new(self.project, current_user, load_lazy_references: load_lazy_references)
+    ext.analyze(text)
+    ext
   end
 
-  def mentioned_users(current_user = nil)
-    return [] if mentionable_text.blank?
-
-    ext = Gitlab::ReferenceExtractor.new(self.project, current_user)
-    ext.analyze(mentionable_text)
-    ext.users.uniq
+  def mentioned_users(current_user = nil, load_lazy_references: true)
+    all_references(current_user, load_lazy_references: load_lazy_references).users
   end
 
   # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference.
-  def references(p = project, current_user = self.author, text = mentionable_text)
+  def referenced_mentionables(current_user = self.author, text = self.mentionable_text, load_lazy_references: true)
     return [] if text.blank?
 
-    ext = Gitlab::ReferenceExtractor.new(p, current_user)
-    ext.analyze(text)
-
-    (ext.issues + ext.merge_requests + ext.commits).uniq - [local_reference]
+    refs = all_references(current_user, text, load_lazy_references: load_lazy_references)
+    (refs.issues + refs.merge_requests + refs.commits) - [local_reference]
   end
 
   # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+.
-  def create_cross_references!(p = project, a = author, without = [])
-    refs = references(p)
-
+  def create_cross_references!(author = self.author, without = [], text = self.mentionable_text)
+    refs = referenced_mentionables(author, text)
+    
     # We're using this method instead of Array diffing because that requires
     # both of the object's `hash` values to be the same, which may not be the
     # case for otherwise identical Commit objects.
-    refs.reject! { |ref| without.include?(ref) }
+    refs.reject! { |ref| without.include?(ref) || cross_reference_exists?(ref) }
 
     refs.each do |ref|
-      SystemNoteService.cross_reference(ref, local_reference, a)
+      SystemNoteService.cross_reference(ref, local_reference, author)
     end
   end
 
   # When a mentionable field is changed, creates cross-reference notes that
   # don't already exist
-  def create_new_cross_references!(p = project, a = author)
+  def create_new_cross_references!(author = self.author)
     changes = detect_mentionable_changes
 
     return if changes.empty?
 
     original_text = changes.collect { |_, vals| vals.first }.join(' ')
 
-    preexisting = references(p, self.author, original_text)
-    create_cross_references!(p, a, preexisting)
+    preexisting = referenced_mentionables(author, original_text)
+    create_cross_references!(author, preexisting)
   end
 
   private
@@ -111,4 +111,10 @@ module Mentionable
     # Only include changed fields that are mentionable
     source.select { |key, val| mentionable.include?(key) }
   end
+  
+  # Determine whether or not a cross-reference Note has already been created between this Mentionable and
+  # the specified target.
+  def cross_reference_exists?(target)
+    SystemNoteService.cross_reference_exists?(target, local_reference)
+  end
 end
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 7c9597333dd943269a8c83b0e4eeb9bd10930555..85367f89f4f381f4f8d46e2266479eacefb86904 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -12,7 +12,7 @@
 #
 #       # ...
 #
-#       participant :author, :assignee, :mentioned_users, :notes
+#       participant :author, :assignee, :notes, ->(current_user) { mentioned_users(current_user) }
 #     end
 #
 #     issue = Issue.last
@@ -27,7 +27,7 @@ module Participable
 
   module ClassMethods
     def participant(*attrs)
-      participant_attrs.concat(attrs.map(&:to_s))
+      participant_attrs.concat(attrs)
     end
 
     def participant_attrs
@@ -37,21 +37,21 @@ module Participable
 
   # Be aware that this method makes a lot of sql queries.
   # Save result into variable if you are going to reuse it inside same request
-  def participants(current_user = self.author, project = self.project)
+  def participants(current_user = self.author, load_lazy_references: true)
     participants = self.class.participant_attrs.flat_map do |attr|
-      meth = method(attr)
-
       value =
-        if meth.arity == 1 || meth.arity == -1
-          meth.call(current_user)
+        if attr.respond_to?(:call)
+          instance_exec(current_user, &attr)
         else
-          meth.call
+          send(attr)
         end
 
-      participants_for(value, current_user, project)
+      participants_for(value, current_user)
     end.compact.uniq
 
-    if project
+    if load_lazy_references
+      participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq
+
       participants.select! do |user|
         user.can?(:read_project, project)
       end
@@ -62,14 +62,14 @@ module Participable
 
   private
 
-  def participants_for(value, current_user = nil, project = nil)
+  def participants_for(value, current_user = nil)
     case value
-    when User
+    when User, Gitlab::Markdown::ReferenceFilter::LazyReference
       [value]
     when Enumerable, ActiveRecord::Relation
-      value.flat_map { |v| participants_for(v, current_user, project) }
+      value.flat_map { |v| participants_for(v, current_user) }
     when Participable
-      value.participants(current_user, project)
+      value.participants(current_user, load_lazy_references: false)
     end
   end
 end
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index 0ad2654867d767ca1e18955ab773d81ce59a23f3..913c747a1c3194bc5c98cd1f9558fdde019b520a 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -8,12 +8,12 @@ module Sortable
   included do
     # By default all models should be ordered
     # by created_at field starting from newest
-    default_scope { order(created_at: :desc, id: :desc) }
+    default_scope { order(id: :desc) }
 
-    scope :order_created_desc, -> { reorder(created_at: :desc, id: :desc) }
-    scope :order_created_asc, -> { reorder(created_at: :asc, id: :asc) }
-    scope :order_updated_desc, -> { reorder(updated_at: :desc, id: :desc) }
-    scope :order_updated_asc, -> { reorder(updated_at: :asc, id: :asc) }
+    scope :order_created_desc, -> { reorder(created_at: :desc) }
+    scope :order_created_asc, -> { reorder(created_at: :asc) }
+    scope :order_updated_desc, -> { reorder(updated_at: :desc) }
+    scope :order_updated_asc, -> { reorder(updated_at: :asc) }
     scope :order_name_asc, -> { reorder(name: :asc) }
     scope :order_name_desc, -> { reorder(name: :desc) }
   end
diff --git a/app/models/group.rb b/app/models/group.rb
index 9cd146bb73bf18e718beab0f34594b7ca7a50c8c..34904af3b5bde7dd1a7f7f9da8dac0d4e4b128c6 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -64,7 +64,7 @@ class Group < Namespace
   end
 
   def owners
-    @owners ||= group_members.owners.map(&:user)
+    @owners ||= group_members.owners.includes(:user).map(&:user)
   end
 
   def add_users(user_ids, access_level, current_user = nil)
@@ -120,7 +120,7 @@ class Group < Namespace
   end
 
   def public_profile?
-    projects.public_only.any?
+    self.public || projects.public_only.any?
   end
 
   def post_create_hook
diff --git a/app/models/group_label.rb b/app/models/group_label.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0fc39cb87716245bbcd36c658f92b2f0eb5e0e37
--- /dev/null
+++ b/app/models/group_label.rb
@@ -0,0 +1,9 @@
+class GroupLabel
+  attr_accessor :title, :labels
+  alias_attribute :name, :title
+
+  def initialize(title, labels)
+    @title = title
+    @labels = labels
+  end
+end
diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb
index 1dd2be68ebf404bcb4f82b48785ef704d2521d0a..91844da62e22c6c7a2b3cfc1f799e301f899fd9c 100644
--- a/app/models/group_milestone.rb
+++ b/app/models/group_milestone.rb
@@ -1,5 +1,5 @@
 class GroupMilestone
-
+  attr_accessor :title, :milestones
   alias_attribute :name, :title
 
   def initialize(title, milestones)
@@ -7,18 +7,10 @@ class GroupMilestone
     @milestones = milestones
   end
 
-  def title
-    @title
-  end
-
   def safe_title
     @title.parameterize
   end
-
-  def milestones
-    @milestones
-  end
-
+  
   def projects
     milestones.map { |milestone| milestone.project }
   end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index fc7e9abe29ea2869079432178f64954e7357c5b2..721831080334cefe53755d5df5d130af734a8931 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -95,4 +95,14 @@ class Issue < ActiveRecord::Base
   def source_project
     project
   end
+
+  # From all notes on this issue, we'll select the system notes about linked
+  # merge requests. Of those, the MRs closing `self` are returned.
+  def closed_by_merge_requests(current_user = nil)
+    return [] unless open?
+
+    notes.system.flat_map do |note|
+      note.all_references(current_user).merge_requests
+    end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
+  end
 end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index c83b15c7d392d355cf166a7358d21f6aac88d5d1..85f37e49e625faed80368f481672782d36dd0b7a 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -40,7 +40,7 @@ class MergeRequest < ActiveRecord::Base
   after_create :create_merge_request_diff
   after_update :update_merge_request_diff
 
-  delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
+  delegate :commits, :diffs, to: :merge_request_diff, prefix: nil
 
   # When this attribute is true some MR validation is ignored
   # It allows us to close or modify broken merge requests
@@ -157,6 +157,18 @@ class MergeRequest < ActiveRecord::Base
     reference
   end
 
+  def last_commit
+    merge_request_diff ? merge_request_diff.last_commit : compare_commits.last
+  end
+
+  def first_commit
+    merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
+  end
+
+  def last_commit_short_sha
+    last_commit.short_id
+  end
+
   def validate_branches
     if target_project == source_project && target_branch == source_branch
       errors.add :branch_conflict, "You can not use same project/branch for source and target"
@@ -222,10 +234,6 @@ class MergeRequest < ActiveRecord::Base
     self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
   end
 
-  def open?
-    opened? || reopened?
-  end
-
   def work_in_progress?
     !!(title =~ /\A\[?WIP\]?:? /i)
   end
@@ -249,7 +257,7 @@ class MergeRequest < ActiveRecord::Base
 
     Note.where(
       "(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" +
-      "(project_id = :source_project_id AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
+      "((project_id = :source_project_id OR project_id = :target_project_id) AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
       mr_id: id,
       commit_ids: commit_ids,
       target_project_id: target_project_id,
@@ -294,6 +302,10 @@ class MergeRequest < ActiveRecord::Base
     target_project
   end
 
+  def closes_issue?(issue)
+    closes_issues.include?(issue)
+  end
+
   # Return the set of issues that will be closed if this merge request is accepted.
   def closes_issues(current_user = self.author)
     if target_branch == project.default_branch
@@ -458,4 +470,10 @@ class MergeRequest < ActiveRecord::Base
       unlock_mr if locked?
     end
   end
+
+  def ci_commit
+    if last_commit
+      source_project.ci_commit(last_commit.id)
+    end
+  end
 end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index c9ef8023aead94befba7fe0959892986c19b8e1c..6575d0bc81f1ea118a37a2f9c7bc8f4191ef2b98 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -55,6 +55,10 @@ class MergeRequestDiff < ActiveRecord::Base
     commits.first
   end
 
+  def first_commit
+    commits.last
+  end
+
   def last_commit_short_sha
     @last_commit_short_sha ||= last_commit.short_id
   end
@@ -163,7 +167,8 @@ class MergeRequestDiff < ActiveRecord::Base
         merge_request.fetch_ref
 
         # Get latest sha of branch from source project
-        source_sha = merge_request.source_project.commit(source_branch).sha
+        source_commit = merge_request.source_project.commit(source_branch)
+        source_sha = source_commit.try(:sha)
 
         Gitlab::CompareResult.new(
           Gitlab::Git::Compare.new(
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 84acba30b6bd1b1ef4f4a483c09dc2ec367c93cb..2ff16e2825c71ff7eb89eb905505048ebeedbd08 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -105,4 +105,36 @@ class Milestone < ActiveRecord::Base
   def author_id
     nil
   end
+
+  # Sorts the issues for the given IDs.
+  #
+  # This method runs a single SQL query using a CASE statement to update the
+  # position of all issues in the current milestone (scoped to the list of IDs).
+  #
+  # Given the ids [10, 20, 30] this method produces a SQL query something like
+  # the following:
+  #
+  #     UPDATE issues
+  #     SET position = CASE
+  #       WHEN id = 10 THEN 1
+  #       WHEN id = 20 THEN 2
+  #       WHEN id = 30 THEN 3
+  #       ELSE position
+  #     END
+  #     WHERE id IN (10, 20, 30);
+  #
+  # This method expects that the IDs given in `ids` are already Fixnums.
+  def sort_issues(ids)
+    pairs = []
+
+    ids.each_with_index do |id, index|
+      pairs << id
+      pairs << index + 1
+    end
+
+    conditions = 'WHEN id = ? THEN ? ' * ids.length
+
+    issues.where(id: ids).
+      update_all(["position = CASE #{conditions} ELSE position END", *pairs])
+  end
 end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index bc8525df5a5097bdf4f793057251a0e15fc182c5..5782e649f8be15100eadec612886b6ea2f26e5ae 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -118,6 +118,8 @@ class Namespace < ActiveRecord::Base
     gitlab_shell.add_namespace(path_was)
 
     if gitlab_shell.mv_namespace(path_was, path)
+      Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
+
       # If repositories moved successfully we need to
       # send update instructions to users.
       # However we cannot allow rollback since we moved namespace dir
diff --git a/app/models/note.rb b/app/models/note.rb
index de3b6df88f7ce8ef07d6c4e8c7db267f2ca28fd3..0b3aa30abd745187bc656439d4cddac7969a532a 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -22,14 +22,14 @@ require 'carrierwave/orm/activerecord'
 require 'file_size_validator'
 
 class Note < ActiveRecord::Base
-  include Mentionable
   include Gitlab::CurrentSettings
   include Participable
+  include Mentionable
 
   default_value_for :system, false
 
   attr_mentionable :note
-  participant :author, :mentioned_users
+  participant :author
 
   belongs_to :project
   belongs_to :noteable, polymorphic: true
@@ -60,9 +60,13 @@ class Note < ActiveRecord::Base
   scope :inc_author_project, ->{ includes(:project, :author) }
   scope :inc_author, ->{ includes(:author) }
 
+  scope :with_associations, -> do
+    includes(:author, :noteable, :updated_by,
+             project: [:project_members, { group: [:group_members] }])
+  end
+
   serialize :st_diff
   before_create :set_diff, if: ->(n) { n.line_code.present? }
-  after_update :set_references
 
   class << self
     def discussions_from_notes(notes)
@@ -333,15 +337,13 @@ class Note < ActiveRecord::Base
   end
 
   def noteable_type_name
-    if noteable_type.present?
-      noteable_type.downcase
-    end
+    noteable_type.downcase if noteable_type.present?
   end
 
   # FIXME: Hack for polymorphic associations with STI
   #        For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
-  def noteable_type=(sType)
-    super(sType.to_s.classify.constantize.base_class.to_s)
+  def noteable_type=(noteable_type)
+    super(noteable_type.to_s.classify.constantize.base_class.to_s)
   end
 
   # Reset notes events cache
@@ -357,10 +359,6 @@ class Note < ActiveRecord::Base
     Event.reset_event_cache_for(self)
   end
 
-  def set_references
-    create_new_cross_references!(project, author)
-  end
-
   def system?
     read_attribute(:system)
   end
diff --git a/app/models/project.rb b/app/models/project.rb
index 021920008ad8c9774e1f4bdd17e49897062258c1..bdb22e49bb5cbd1c9c26c8387d38af5f1c822022 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -37,6 +37,7 @@ class Project < ActiveRecord::Base
   include Gitlab::ConfigHelper
   include Gitlab::ShellAdapter
   include Gitlab::VisibilityLevel
+  include Gitlab::CurrentSettings
   include Referable
   include Sortable
   include AfterCommitQueue
@@ -119,8 +120,9 @@ class Project < ActiveRecord::Base
   has_many :deploy_keys, through: :deploy_keys_projects
   has_many :users_star_projects, dependent: :destroy
   has_many :starrers, through: :users_star_projects, source: :user
-  has_many :ci_commits, ->() { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
+  has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
   has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build'
+  has_many :releases, dependent: :destroy
 
   has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
   has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id
@@ -243,11 +245,12 @@ class Project < ActiveRecord::Base
       # Use of unscoped ensures we're not secretly adding any ORDER BYs, which
       # have a negative impact on performance (and aren't needed for this
       # query).
-      unscoped.
+      projects = unscoped.
         joins(:namespace).
-        iwhere('namespaces.path' => namespace_path).
-        iwhere('projects.path' => project_path).
-        take
+        iwhere('namespaces.path' => namespace_path)
+
+      projects.where('projects.path' => project_path).take ||
+        projects.iwhere('projects.path' => project_path).take
     end
 
     def visibility_levels
@@ -567,7 +570,7 @@ class Project < ActiveRecord::Base
   end
 
   def empty_repo?
-    !repository.exists? || repository.empty?
+    !repository.exists? || !repository.has_visible_content?
   end
 
   def repo
@@ -656,6 +659,8 @@ class Project < ActiveRecord::Base
       # db changes in order to prevent out of sync between db and fs
       raise Exception.new('repository cannot be renamed')
     end
+
+    Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path)
   end
 
   def hook_attrs
@@ -772,7 +777,9 @@ class Project < ActiveRecord::Base
   end
 
   def ensure_gitlab_ci_project
-    gitlab_ci_project || create_gitlab_ci_project
+    gitlab_ci_project || create_gitlab_ci_project(
+      shared_runners_enabled: current_application_settings.shared_runners_enabled
+    )
   end
 
   def enable_ci
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index 5f5255ab48727398a285a8d2247b222189de587a..d31b12f539e9bb9dee0e3903761d418f7d2004e4 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -48,7 +48,7 @@ class BambooService < CiService
   end
 
   def reset_password
-    if prop_updated?(:bamboo_url)
+    if bamboo_url_changed? && !password_touched?
       self.password = nil
     end
   end
diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb
index cbf325cc5255af0193bbb25155db43de7fe91767..d89466b689f8aeda158c3ea655bf8d5b4f8a6a33 100644
--- a/app/models/project_services/ci/hip_chat_message.rb
+++ b/app/models/project_services/ci/hip_chat_message.rb
@@ -11,7 +11,7 @@ module Ci
     def to_s
       lines = Array.new
       lines.push("<a href=\"#{ci_project_url(project)}\">#{project.name}</a> - ")
-      lines.push("<a href=\"#{ci_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}\">Commit ##{commit.id}</a></br>")
+      lines.push("<a href=\"#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}\">Commit ##{commit.id}</a></br>")
       lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}</br>")
       lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).")
       lines.join('')
diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb
index dc050a3fc59973a8890e5f6e09daa833af3931e4..1a6ff8e34c98af15048640b6b9a6bab7fcad0f2b 100644
--- a/app/models/project_services/ci/slack_message.rb
+++ b/app/models/project_services/ci/slack_message.rb
@@ -45,7 +45,7 @@ module Ci
 
     def attachment_message
       out = "<#{ci_project_url(project)}|#{project_name}>: "
-      out << "Commit <#{ci_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> "
+      out << "Commit <#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> "
       out << "(<#{commit_sha_link}|#{commit.short_sha}>) "
       out << "of <#{commit_ref_link}|#{commit.ref}> "
       out << "by #{commit.git_author_name} " if commit.git_author_name
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index c73c4b058a1bf8fc248368932c96ca22ba4d3afb..c240213200ddb077006042f0c9c17d18e8154daf 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -32,7 +32,6 @@ class DroneCiService < CiService
 
   def compose_service_hook
     hook = service_hook || build_service_hook
-    hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join
     hook.enable_ssl_verification = enable_ssl_verification
     hook.save
   end
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 4dcd16ede3a29d5cb835b51860177747068b316c..095d04e0df4c0a2959e796d849ab583cdfa487c8 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -71,7 +71,7 @@ class GitlabCiService < CiService
 
   def build_page(sha, ref)
     if project.gitlab_ci_project.present?
-      ci_namespace_project_commit_url(project.namespace, project, sha)
+      builds_namespace_project_commit_url(project.namespace, project, sha)
     end
   end
 
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index fb11cad352ed014e3ac818a6bb2cbbf3c24d04ac..0b0224612505e18f81386b5442103193865f5d29 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -45,7 +45,7 @@ class TeamcityService < CiService
   end
 
   def reset_password
-    if prop_updated?(:teamcity_url)
+    if teamcity_url_changed? && !password_touched?
       self.password = nil
     end
   end
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index f602a965364e6e1584e689d35b7f734b07575e14..9f380a382cb40f0acaf855d659ebd034903b26a2 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -139,15 +139,28 @@ class ProjectTeam
     Gitlab::Access.options.key max_member_access(user_id)
   end
 
+  # This method assumes project and group members are eager loaded for optimal
+  # performance.
   def max_member_access(user_id)
     access = []
-    access << project.project_members.find_by(user_id: user_id).try(:access_field)
+
+    project.project_members.each do |member|
+      if member.user_id == user_id
+        access << member.access_field if member.access_field
+        break
+      end
+    end
 
     if group
-      access << group.group_members.find_by(user_id: user_id).try(:access_field)
+      group.group_members.each do |member|
+        if member.user_id == user_id
+          access << member.access_field if member.access_field
+          break
+        end
+      end
     end
 
-    access.compact.max
+    access.max
   end
 
   private
diff --git a/app/models/release.rb b/app/models/release.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e196b84eb18845cbbfdf8b2acedd6309ab3931f2
--- /dev/null
+++ b/app/models/release.rb
@@ -0,0 +1,5 @@
+class Release < ActiveRecord::Base
+  belongs_to :project
+
+  validates :description, :project, :tag, presence: true
+end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 8cd182e1b0bd0a044e84aaa5507207c6160165fe..622d6303bfe75912baa9cbf3d733fb8653005750 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -8,6 +8,14 @@ class Repository
 
   attr_accessor :raw_repository, :path_with_namespace, :project
 
+  def self.clean_old_archives
+    repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path
+
+    return unless File.directory?(repository_downloads_path)
+
+    Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
+  end
+
   def initialize(path_with_namespace, default_branch = nil, project = nil)
     @path_with_namespace = path_with_namespace
     @project = project
@@ -36,6 +44,19 @@ class Repository
     raw_repository.empty?
   end
 
+  #
+  # Git repository can contains some hidden refs like:
+  #   /refs/notes/*
+  #   /refs/git-as-svn/*
+  #   /refs/pulls/*
+  # This refs by default not visible in project page and not cloned to client side.
+  #
+  # This method return true if repository contains some content visible in project page.
+  #
+  def has_visible_content?
+    !raw_repository.branches.empty?
+  end
+
   def commit(id = 'HEAD')
     return nil unless raw_repository
     commit = Gitlab::Git::Commit.find(raw_repository, id)
@@ -46,13 +67,16 @@ class Repository
   end
 
   def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
-    commits = Gitlab::Git::Commit.where(
+    options = {
       repo: raw_repository,
       ref: ref,
       path: path,
       limit: limit,
       offset: offset,
-    )
+      follow: path.present?
+    }
+
+    commits = Gitlab::Git::Commit.where(options)
     commits = Commit.decorate(commits, @project) if commits.present?
     commits
   end
@@ -63,6 +87,15 @@ class Repository
     commits
   end
 
+  def find_commits_by_message(query)
+    # Limited to 1000 commits for now, could be parameterized?
+    args = %W(#{Gitlab.config.git.bin_path} log --pretty=%H --max-count 1000 --grep=#{query})
+
+    git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp)
+    commits = git_log_results.map { |c| commit(c) }
+    commits
+  end
+
   def find_branch(name)
     branches.find { |branch| branch.name == name }
   end
@@ -271,20 +304,12 @@ class Repository
   end
 
   def last_commit_for_path(sha, path)
-    args = %W(git rev-list --max-count=1 #{sha} -- #{path})
+    args = %W(#{Gitlab.config.git.bin_path} rev-list --max-count=1 #{sha} -- #{path})
     sha = Gitlab::Popen.popen(args, path_to_repo).first.strip
     commit(sha)
   end
 
   # Remove archives older than 2 hours
-  def clean_old_archives
-    repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path
-
-    return unless File.directory?(repository_downloads_path)
-
-    Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
-  end
-
   def branches_sorted_by(value)
     case value
     when 'recently_updated'
@@ -320,13 +345,7 @@ class Repository
   end
 
   def blob_for_diff(commit, diff)
-    file = blob_at(commit.id, diff.new_path)
-
-    unless file
-      file = prev_blob_for_diff(commit, diff)
-    end
-
-    file
+    blob_at(commit.id, diff.file_path)
   end
 
   def prev_blob_for_diff(commit, diff)
@@ -336,7 +355,7 @@ class Repository
   end
 
   def branch_names_contains(sha)
-    args = %W(git branch --contains #{sha})
+    args = %W(#{Gitlab.config.git.bin_path} branch --contains #{sha})
     names = Gitlab::Popen.popen(args, path_to_repo).first
 
     if names.respond_to?(:split)
@@ -353,7 +372,7 @@ class Repository
   end
 
   def tag_names_contains(sha)
-    args = %W(git tag --contains #{sha})
+    args = %W(#{Gitlab.config.git.bin_path} tag --contains #{sha})
     names = Gitlab::Popen.popen(args, path_to_repo).first
 
     if names.respond_to?(:split)
@@ -488,9 +507,13 @@ class Repository
     end
   end
 
+  def merge_base(first_commit_id, second_commit_id)
+    rugged.merge_base(first_commit_id, second_commit_id)
+  end
+
   def search_files(query, ref)
     offset = 2
-    args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} #{query} #{ref || root_ref})
+    args = %W(#{Gitlab.config.git.bin_path} grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref})
     Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
   end
 
@@ -522,7 +545,7 @@ class Repository
   end
 
   def fetch_ref(source_path, source_ref, target_ref)
-    args = %W(git fetch #{source_path} #{source_ref}:#{target_ref})
+    args = %W(#{Gitlab.config.git.bin_path} fetch -f #{source_path} #{source_ref}:#{target_ref})
     Gitlab::Popen.popen(args, path_to_repo)
   end
 
diff --git a/app/models/service.rb b/app/models/service.rb
index 7e845d565b1c4c7cdcc712d89621b49d2f37885a..d610abd16837ce1760d2a4585050f2b0b10f0944 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -33,6 +33,8 @@ class Service < ActiveRecord::Base
 
   after_initialize :initialize_properties
 
+  after_commit :reset_updated_properties
+
   belongs_to :project
   has_one :service_hook
 
@@ -103,6 +105,7 @@ class Service < ActiveRecord::Base
 
   # Provide convenient accessor methods
   # for each serialized property.
+  # Also keep track of updated properties in a similar way as ActiveModel::Dirty
   def self.prop_accessor(*args)
     args.each do |arg|
       class_eval %{
@@ -111,21 +114,39 @@ class Service < ActiveRecord::Base
         end
 
         def #{arg}=(value)
+          updated_properties['#{arg}'] = #{arg} unless #{arg}_changed?
           self.properties['#{arg}'] = value
         end
+
+        def #{arg}_changed?
+          #{arg}_touched? && #{arg} != #{arg}_was
+        end
+
+        def #{arg}_touched?
+          updated_properties.include?('#{arg}')
+        end
+
+        def #{arg}_was
+          updated_properties['#{arg}']
+        end
       }
     end
   end
 
-  # ActiveRecord does not provide a mechanism to track changes in serialized keys.
-  # This is why we need to perform extra query to do it mannually.
-  def prop_updated?(prop_name)
-    relation_name = self.type.underscore
-    previous_value = project.send(relation_name).send(prop_name)
-    return false if previous_value.nil?
-    previous_value != send(prop_name)
+  # Returns a hash of the properties that have been assigned a new value since last save,
+  # indicating their original values (attr => original value).
+  # ActiveRecord does not provide a mechanism to track changes in serialized keys, 
+  # so we need a specific implementation for service properties.
+  # This allows to track changes to properties set with the accessor methods,
+  # but not direct manipulation of properties hash.
+  def updated_properties
+    @updated_properties ||= ActiveSupport::HashWithIndifferentAccess.new
   end
 
+  def reset_updated_properties
+    @updated_properties = nil
+  end
+  
   def async_execute(data)
     return unless supported_events.include?(data[:object_kind])
 
diff --git a/app/models/user.rb b/app/models/user.rb
index 889d2d3b867e5e642a8e89f42f70568ecdb5e3dd..67fef1c1e6a19b40409769f1950f704b35b7a5d7 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -68,6 +68,7 @@ class User < ActiveRecord::Base
   include Referable
   include Sortable
   include TokenAuthenticatable
+  include CaseSensitivity
 
   default_value_for :admin, false
   default_value_for :can_create_group, gitlab_config.default_can_create_group
@@ -182,7 +183,7 @@ class User < ActiveRecord::Base
 
   # User's Project preference
   # Note: When adding an option, it MUST go on the end of the array.
-  enum project_view: [:readme, :activity]
+  enum project_view: [:readme, :activity, :files]
 
   alias_attribute :private_token, :authentication_token
 
@@ -234,21 +235,16 @@ class User < ActiveRecord::Base
 
     # Find a User by their primary email or any associated secondary email
     def find_by_any_email(email)
-      user_table = arel_table
-      email_table = Email.arel_table
-
-      # Use ARel to build a query:
-      query = user_table.
-        # SELECT "users".* FROM "users"
-        project(user_table[Arel.star]).
-        # LEFT OUTER JOIN "emails"
-        join(email_table, Arel::Nodes::OuterJoin).
-        # ON "users"."id" = "emails"."user_id"
-        on(user_table[:id].eq(email_table[:user_id])).
-        # WHERE ("user"."email" = '<email>' OR "emails"."email" = '<email>')
-        where(user_table[:email].eq(email).or(email_table[:email].eq(email)))
-
-      find_by_sql(query.to_sql).first
+      sql = 'SELECT *
+      FROM users
+      WHERE id IN (
+        SELECT id FROM users WHERE email = :email
+        UNION
+        SELECT emails.user_id FROM emails WHERE email = :email
+      )
+      LIMIT 1;'
+
+      User.find_by_sql([sql, { email: email }]).first
     end
 
     def filter(filter_name)
@@ -273,8 +269,13 @@ class User < ActiveRecord::Base
     end
 
     def by_login(login)
-      where('lower(username) = :value OR lower(email) = :value',
-            value: login.to_s.downcase).first
+      return nil unless login
+
+      if login.include?('@'.freeze)
+        unscoped.iwhere(email: login).take
+      else
+        unscoped.iwhere(username: login).take
+      end
     end
 
     def find_by_username!(username)
@@ -395,15 +396,17 @@ class User < ActiveRecord::Base
                            end
   end
 
+  def authorized_projects_id
+    @authorized_projects_id ||= begin
+      project_ids = personal_projects.pluck(:id)
+      project_ids.push(*groups_projects.pluck(:id))
+      project_ids.push(*projects.pluck(:id).uniq)
+    end
+  end
 
   # Projects user has access to
   def authorized_projects
-    @authorized_projects ||= begin
-                               project_ids = personal_projects.pluck(:id)
-                               project_ids.push(*groups_projects.pluck(:id))
-                               project_ids.push(*projects.pluck(:id).uniq)
-                               Project.where(id: project_ids)
-                             end
+    @authorized_projects ||= Project.where(id: authorized_projects_id)
   end
 
   def owned_projects
@@ -700,12 +703,15 @@ class User < ActiveRecord::Base
   end
 
   def toggle_star(project)
-    user_star_project = users_star_projects.
-      where(project: project, user: self).take
-    if user_star_project
-      user_star_project.destroy
-    else
-      UsersStarProject.create!(project: project, user: self)
+    UsersStarProject.transaction do
+      user_star_project = users_star_projects.
+          where(project: project, user: self).lock(true).first
+
+      if user_star_project
+        user_star_project.destroy
+      else
+        UsersStarProject.create!(project: project, user: self)
+      end
     end
   end
 
@@ -759,11 +765,14 @@ class User < ActiveRecord::Base
   end
 
   def ci_authorized_projects
-    @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects)
+    @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects_id)
   end
 
   def ci_authorized_runners
-    Ci::Runner.specific.includes(:runner_projects).
-      where(ci_runner_projects: { project_id: ci_authorized_projects } )
+    @ci_authorized_runners ||= begin
+      runner_ids = Ci::RunnerProject.joins(:project).
+        where(ci_projects: { gitlab_id: authorized_projects_id }).select(:runner_id)
+      Ci::Runner.specific.where(id: runner_ids)
+    end
   end
 end
diff --git a/app/services/archive_repository_service.rb b/app/services/archive_repository_service.rb
index e1b41527d8dc5d5efb86d47a1f11186de9845a6c..2160bf13e6d281cbc073e3e827d4bdee8d7558f3 100644
--- a/app/services/archive_repository_service.rb
+++ b/app/services/archive_repository_service.rb
@@ -7,19 +7,12 @@ class ArchiveRepositoryService
   end
 
   def execute(options = {})
-    project.repository.clean_old_archives
+    RepositoryArchiveCacheWorker.perform_async
 
-    raise "No archive file path" unless file_path
+    metadata = project.repository.archive_metadata(ref, storage_path, format)
+    raise "Repository or ref not found" if metadata.empty?
 
-    return file_path if archived?
-
-    unless archiving?
-      RepositoryArchiveWorker.perform_async(project.id, ref, format)
-    end
-
-    archived = wait_until_archived(options[:timeout] || 5.0)
-
-    file_path if archived
+    metadata
   end
 
   private
@@ -27,36 +20,4 @@ class ArchiveRepositoryService
   def storage_path
     Gitlab.config.gitlab.repository_downloads_path
   end
-
-  def file_path
-    @file_path ||= project.repository.archive_file_path(ref, storage_path, format)
-  end
-
-  def pid_file_path
-    @pid_file_path ||= project.repository.archive_pid_file_path(ref, storage_path, format)
-  end
-
-  def archived?
-    File.exist?(file_path)
-  end
-
-  def archiving?
-    File.exist?(pid_file_path)
-  end
-
-  def wait_until_archived(timeout = 5.0)
-    return archived? if timeout == 0.0
-    
-    t1 = Time.now
-
-    begin
-      sleep 0.1
-
-      success = archived?
-
-      t2 = Time.now
-    end until success || t2 - t1 >= timeout
-
-    success
-  end
 end
diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb
index c420f3268fd322444d173c0abb26882800ba573e..912eb6258a4aea905ee4b5ff1da73969e7a2a472 100644
--- a/app/services/ci/create_builds_service.rb
+++ b/app/services/ci/create_builds_service.rb
@@ -1,8 +1,20 @@
 module Ci
   class CreateBuildsService
-    def execute(commit, stage, ref, tag, user, trigger_request)
+    def execute(commit, stage, ref, tag, user, trigger_request, status)
       builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag)
 
+      # check when to create next build
+      builds_attrs = builds_attrs.select do |build_attrs|
+        case build_attrs[:when]
+        when 'on_success'
+          status == 'success'
+        when 'on_failure'
+          status == 'failed'
+        when 'always'
+          %w(success failed).include?(status)
+        end
+      end
+
       builds_attrs.map do |build_attrs|
         # don't create the same build twice
         unless commit.builds.find_by(ref: ref, tag: tag, trigger_request: trigger_request, name: build_attrs[:name])
diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb
index b95835ba0931ddeab2fa21407bd622ed05a4fc41..b8d241930351e5624e872c27910e41b2b9648ed2 100644
--- a/app/services/ci/image_for_build_service.rb
+++ b/app/services/ci/image_for_build_service.rb
@@ -1,17 +1,15 @@
 module Ci
   class ImageForBuildService
     def execute(project, params)
-      image_name =
-        if params[:sha]
-          commit = project.commits.find_by(sha: params[:sha])
-          image_for_commit(commit)
-        elsif params[:ref]
-          commit = project.last_commit_for_ref(params[:ref])
-          image_for_commit(commit)
-        else
-          'build-unknown.svg'
+      sha = params[:sha]
+      sha ||=
+        if params[:ref]
+          project.gl_project.commit(params[:ref]).try(:sha)
         end
 
+      commit = project.commits.ordered.find_by(sha: sha)
+      image_name = image_for_commit(commit)
+
       image_path = Rails.root.join('public/ci', image_name)
 
       OpenStruct.new(
diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb
index 71b61bbe389bd957b28c318c31fa0c52f65f7b65..7beb098659c44f70a2e865133fa64e7b85832d26 100644
--- a/app/services/ci/register_build_service.rb
+++ b/app/services/ci/register_build_service.rb
@@ -17,7 +17,7 @@ module Ci
       builds = builds.order('created_at ASC')
 
       build = builds.find do |build|
-        (build.tag_list - current_runner.tag_list).empty?
+        build.can_be_served?(current_runner)
       end
 
 
diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb
index 1a7318048b3323ce8a32327cb7638c2d3e4570df..9917119fce2499cca0eaa732421d8e15bd92051d 100644
--- a/app/services/create_tag_service.rb
+++ b/app/services/create_tag_service.rb
@@ -1,7 +1,7 @@
 require_relative 'base_service'
 
 class CreateTagService < BaseService
-  def execute(tag_name, ref, message)
+  def execute(tag_name, ref, message, release_description = nil)
     valid_tag = Gitlab::GitRefValidator.validate(tag_name)
     if valid_tag == false
       return error('Tag name invalid')
@@ -19,8 +19,12 @@ class CreateTagService < BaseService
     new_tag = repository.find_tag(tag_name)
 
     if new_tag
-      push_data = create_push_data(project, current_user, new_tag)
+      if release_description
+        release = project.releases.find_or_initialize_by(tag: tag_name)
+        release.update_attributes(description: release_description)
+      end
 
+      push_data = create_push_data(project, current_user, new_tag)
       EventCreateService.new.push(project, current_user, push_data)
       project.execute_hooks(push_data.dup, :tag_push_hooks)
       project.execute_services(push_data.dup, :tag_push_hooks)
diff --git a/app/services/delete_tag_service.rb b/app/services/delete_tag_service.rb
index 0c8364011367b419d2762da155000afd86d8ec37..de3352a6756c3ad731a89a967a32f7be0f4640b0 100644
--- a/app/services/delete_tag_service.rb
+++ b/app/services/delete_tag_service.rb
@@ -11,8 +11,10 @@ class DeleteTagService < BaseService
     end
 
     if repository.rm_tag(tag_name)
+      release = project.releases.find_by(tag: tag_name)
+      release.destroy if release
+
       push_data = build_push_data(tag)
-      
       EventCreateService.new.push(project, current_user, push_data)
       project.execute_hooks(push_data.dup, :tag_push_hooks)
       project.execute_services(push_data.dup, :tag_push_hooks)
diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb
index 71272fb57075e0be96b0e702585f4564cbcced5a..6107254a34ee01b9bdc4484c22bd0a96b2f1c973 100644
--- a/app/services/files/create_dir_service.rb
+++ b/app/services/files/create_dir_service.rb
@@ -5,5 +5,16 @@ module Files
     def commit
       repository.commit_dir(current_user, @file_path, @commit_message, @target_branch)
     end
+
+    def validate
+      super
+
+      unless @file_path =~ Gitlab::Regex.file_path_regex
+        raise_error(
+          'Your changes could not be committed, because the file path ' +
+          Gitlab::Regex.file_path_regex_message
+        )
+      end
+    end
   end
 end
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index c8e3a910bbade941f73101bc168579d273c45534..2348920cc58c25a1900f3a40dd46e3f56dcee36f 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -9,12 +9,17 @@ module Files
     def validate
       super
 
-      file_name = File.basename(@file_path)
+      if @file_path =~ Gitlab::Regex.directory_traversal_regex
+        raise_error(
+          'Your changes could not be committed, because the file name ' +
+          Gitlab::Regex.directory_traversal_regex_message
+        )
+      end
 
-      unless file_name =~ Gitlab::Regex.file_name_regex
+      unless @file_path =~ Gitlab::Regex.file_path_regex
         raise_error(
           'Your changes could not be committed, because the file name ' +
-          Gitlab::Regex.file_name_regex_message
+          Gitlab::Regex.file_path_regex_message
         )
       end
 
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index f9a8265d2d4d191a2cb86afcec4ec39dfca7d253..3de7bb9dcaaa80f2ef3b6f5baf00e10c0f573e0b 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -49,10 +49,13 @@ class GitPushService
     elsif push_to_existing_branch?(ref, oldrev)
       # Collect data for this git push
       @push_commits = project.repository.commits_between(oldrev, newrev)
-      project.update_merge_requests(oldrev, newrev, ref, @user)
       process_commit_messages(ref)
     end
 
+    # Update merge requests that may be affected by this push. A new branch
+    # could cause the last commit of a merge request to change.
+    project.update_merge_requests(oldrev, newrev, ref, @user)
+
     @push_data = build_push_data(oldrev, newrev, ref)
 
     # If CI was disabled but .gitlab-ci.yml file was pushed
@@ -74,48 +77,30 @@ class GitPushService
   def process_commit_messages(ref)
     is_default_branch = is_default_branch?(ref)
 
-    @push_commits.each do |commit|
-      # Close issues if these commits were pushed to the project's default branch and the commit message matches the
-      # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
-      # a different branch.
-      issues_to_close = commit.closes_issues(user)
+    authors = Hash.new do |hash, commit|
+      email = commit.author_email
+      next hash[email] if hash.has_key?(email)
 
-      # Load commit author only if needed.
-      # For push with 1k commits it prevents 900+ requests in database
-      author = nil
+      hash[email] = commit_user(commit)
+    end
 
+    @push_commits.each do |commit|
       # Keep track of the issues that will be actually closed because they are on a default branch.
       # Hence, when creating cross-reference notes, the not-closed issues (on non-default branches)
       # will also have cross-reference.
-      actually_closed_issues = []
-
-      if issues_to_close.present? && is_default_branch
-        author ||= commit_user(commit)
-        actually_closed_issues = issues_to_close
-        issues_to_close.each do |issue|
-          Issues::CloseService.new(project, author, {}).execute(issue, commit)
+      closed_issues = []
+
+      if is_default_branch
+        # Close issues if these commits were pushed to the project's default branch and the commit message matches the
+        # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
+        # a different branch.
+        closed_issues = commit.closes_issues(user)
+        closed_issues.each do |issue|
+          Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
         end
       end
 
-      if project.default_issues_tracker?
-        create_cross_reference_notes(commit, actually_closed_issues)
-      end
-    end
-  end
-
-  def create_cross_reference_notes(commit, issues_to_close)
-    # Create cross-reference notes for any other references than those given in issues_to_close.
-    # Omit any issues that were referenced in an issue-closing phrase, or have already been
-    # mentioned from this commit (probably from this commit being pushed to a different branch).
-    refs = commit.references(project, user) - issues_to_close
-    refs.reject! { |r| commit.has_mentioned?(r) }
-
-    if refs.present?
-      author ||= commit_user(commit)
-
-      refs.each do |r|
-        SystemNoteService.cross_reference(r, commit, author)
-      end
+      commit.create_cross_references!(authors[commit], closed_issues)
     end
   end
 
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 1ea4b72216c6a2e9edeaa682c43b4da8b1e21686..bcb380d3215da95c3b07a1f38105283a8b620be6 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -10,7 +10,7 @@ module Issues
         issue.update_attributes(label_ids: label_params)
         notification_service.new_issue(issue, current_user)
         event_service.open_issue(issue, current_user)
-        issue.create_cross_references!(issue.project, current_user)
+        issue.create_cross_references!(current_user)
         execute_hooks(issue, 'open')
       end
 
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 2fc6ef7f35642913776680d7e9283bd1d9a21c65..551325e4cab43d4ed9bee2630b348b267aa523fb 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -35,7 +35,7 @@ module Issues
           create_title_change_note(issue, issue.previous_changes['title'].first)
         end
 
-        issue.create_new_cross_references!(issue.project, current_user)
+        issue.create_new_cross_references!(current_user)
         execute_hooks(issue, 'update')
       end
 
diff --git a/app/services/labels/group_service.rb b/app/services/labels/group_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b26cee24d56bbd1ab330744e74cc0b558a2f6fc8
--- /dev/null
+++ b/app/services/labels/group_service.rb
@@ -0,0 +1,26 @@
+module Labels
+  class GroupService < ::BaseService
+    def initialize(project_labels)
+      @project_labels = project_labels.group_by(&:title)
+    end
+
+    def execute
+      build(@project_labels)
+    end
+
+    def label(title)
+      if title
+        group_label = @project_labels[title].group_by(&:title)
+        build(group_label).first
+      else
+        nil
+      end
+    end
+
+    private
+
+    def build(label)
+      label.map { |title, labels| GroupLabel.new(title, labels) }
+    end
+  end
+end
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index 9651b16462cb61b2225111e1b1c6c6a99ac0b98d..009d5a6867efab83a9065e9fcc062683dfcc446a 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -18,7 +18,7 @@ module MergeRequests
         merge_request.update_attributes(label_ids: label_params)
         event_service.open_mr(merge_request, current_user)
         notification_service.new_merge_request(merge_request, current_user)
-        merge_request.create_cross_references!(merge_request.project, current_user)
+        merge_request.create_cross_references!(current_user)
         execute_hooks(merge_request)
       end
 
diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb
index aceb8cb902199cca00d3113fb2210800b2aa17e5..8f25c5e24967dc601fc62f1007fc5ec3296f979a 100644
--- a/app/services/merge_requests/post_merge_service.rb
+++ b/app/services/merge_requests/post_merge_service.rb
@@ -6,6 +6,7 @@ module MergeRequests
   #
   class PostMergeService < MergeRequests::BaseService
     def execute(merge_request)
+      close_issues(merge_request)
       merge_request.mark_as_merged
       create_merge_event(merge_request, current_user)
       create_note(merge_request)
@@ -15,6 +16,15 @@ module MergeRequests
 
     private
 
+    def close_issues(merge_request)
+      return unless merge_request.target_branch == project.default_branch
+
+      closed_issues = merge_request.closes_issues(current_user)
+      closed_issues.each do |issue|
+        Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request)
+      end
+    end
+
     def create_merge_event(merge_request, current_user)
       EventCreateService.new.merge_mr(merge_request, current_user)
     end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index e903e48e3cd26daeb577dc4e5ecb3986dd315515..e180edb4bf3b2f7609748cc36c05ac06a44de2fb 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -5,13 +5,20 @@ module MergeRequests
 
       @oldrev, @newrev = oldrev, newrev
       @branch_name = Gitlab::Git.ref_name(ref)
-      @fork_merge_requests = @project.fork_merge_requests.opened
-      @commits = @project.repository.commits_between(oldrev, newrev)
 
+      find_new_commits
+      # Be sure to close outstanding MRs before reloading them to avoid generating an
+      # empty diff during a manual merge
       close_merge_requests
       reload_merge_requests
-      execute_mr_web_hooks
+
+      # Leave a system note if a branch was deleted/added
+      if branch_added? || branch_removed?
+        comment_mr_branch_presence_changed
+      end
+
       comment_mr_with_commits
+      execute_mr_web_hooks
 
       true
     end
@@ -31,7 +38,6 @@ module MergeRequests
         commit_ids.include?(merge_request.last_commit.id)
       end
 
-
       merge_requests.uniq.select(&:source_project).each do |merge_request|
         MergeRequests::PostMergeService.
           new(merge_request.target_project, @current_user).
@@ -47,7 +53,7 @@ module MergeRequests
     # Note: we should update merge requests from forks too
     def reload_merge_requests
       merge_requests = @project.merge_requests.opened.by_branch(@branch_name).to_a
-      merge_requests += @fork_merge_requests.by_branch(@branch_name).to_a
+      merge_requests += fork_merge_requests.by_branch(@branch_name).to_a
       merge_requests = filter_merge_requests(merge_requests)
 
       merge_requests.each do |merge_request|
@@ -70,13 +76,48 @@ module MergeRequests
       end
     end
 
+    def find_new_commits
+      if branch_added?
+        @commits = []
+
+        merge_request = merge_requests_for_source_branch.first
+        return unless merge_request
+
+        last_commit = merge_request.last_commit
+
+        begin
+          # Since any number of commits could have been made to the restored branch,
+          # find the common root to see what has been added.
+          common_ref = @project.repository.merge_base(last_commit.id, @newrev)
+          # If the a commit no longer exists in this repo, gitlab_git throws
+          # a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52
+          @commits = @project.repository.commits_between(common_ref, @newrev) if common_ref
+        rescue
+        end
+      elsif branch_removed?
+        # No commits for a deleted branch.
+        @commits = []
+      else
+        @commits = @project.repository.commits_between(@oldrev, @newrev)
+      end
+    end
+
+    # Add comment about branches being deleted or added to merge requests
+    def comment_mr_branch_presence_changed
+      presence = branch_added? ? :add : :delete
+
+      merge_requests_for_source_branch.each do |merge_request|
+        SystemNoteService.change_branch_presence(
+            merge_request, merge_request.project, @current_user,
+            :source, @branch_name, presence)
+      end
+    end
+
     # Add comment about pushing new commits to merge requests
     def comment_mr_with_commits
-      merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
-      merge_requests += @fork_merge_requests.where(source_branch: @branch_name).to_a
-      merge_requests = filter_merge_requests(merge_requests)
+      return unless @commits.present?
 
-      merge_requests.each do |merge_request|
+      merge_requests_for_source_branch.each do |merge_request|
         mr_commit_ids = Set.new(merge_request.commits.map(&:id))
 
         new_commits, existing_commits = @commits.partition do |commit|
@@ -91,14 +132,7 @@ module MergeRequests
 
     # Call merge request webhook with update branches
     def execute_mr_web_hooks
-      merge_requests = @project.origin_merge_requests.opened
-        .where(source_branch: @branch_name)
-        .to_a
-      merge_requests += @fork_merge_requests.where(source_branch: @branch_name)
-        .to_a
-      merge_requests = filter_merge_requests(merge_requests)
-
-      merge_requests.each do |merge_request|
+      merge_requests_for_source_branch.each do |merge_request|
         execute_hooks(merge_request, 'update')
       end
     end
@@ -106,5 +140,25 @@ module MergeRequests
     def filter_merge_requests(merge_requests)
       merge_requests.uniq.select(&:source_project)
     end
+
+    def merge_requests_for_source_branch
+      @source_merge_requests ||= begin
+        merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
+        merge_requests += fork_merge_requests.where(source_branch: @branch_name).to_a
+        filter_merge_requests(merge_requests)
+      end
+    end
+
+    def fork_merge_requests
+      @fork_merge_requests ||= @project.fork_merge_requests.opened
+    end
+
+    def branch_added?
+      Gitlab::Git.blank_ref?(@oldrev)
+    end
+
+    def branch_removed?
+      Gitlab::Git.blank_ref?(@newrev)
+    end
   end
 end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 25d79e22e395c9182f508621f7666d05ee63751b..61f7d2bbe895fc3d197c46a2b23320bb6aca2ce9 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -59,7 +59,7 @@ module MergeRequests
           merge_request.mark_as_unchecked
         end
 
-        merge_request.create_new_cross_references!(merge_request.project, current_user)
+        merge_request.create_new_cross_references!(current_user)
         execute_hooks(merge_request, 'update')
       end
 
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 482c0444049bdef61d8c8fab5e4b0fac8779a265..2001dc89c3332fbbf91770d2dc31aefb7d244eb9 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -11,13 +11,7 @@ module Notes
         # Skip system notes, like status changes and cross-references.
         unless note.system
           event_service.leave_note(note, note.author)
-
-          # Create a cross-reference note if this Note contains GFM that names an
-          # issue, merge request, or commit.
-          note.references.each do |mentioned|
-            SystemNoteService.cross_reference(mentioned, note.noteable, note.author)
-          end
-
+          note.create_cross_references!
           execute_hooks(note)
         end
       end
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
index c22a9333ef6889d9f36a1992a9b675b5cb7d92da..72e2f78008de5772036e840a506a8ea760e41853 100644
--- a/app/services/notes/update_service.rb
+++ b/app/services/notes/update_service.rb
@@ -4,7 +4,7 @@ module Notes
       return note unless note.editable?
 
       note.update_attributes(params.merge(updated_by: current_user))
-
+      note.create_new_cross_references!(current_user)
       note.reset_events_cache
 
       note
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index faf1ee008e7e4461fb96eb0055de4f577e7d2bb7..5b84527eccfcb6fb9b40475c8c6782090494552c 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -94,8 +94,6 @@ module Projects
         @project.team << [current_user, :master, current_user]
       end
 
-      @project.update_column(:last_activity_at, @project.created_at)
-
       if @project.import?
         @project.import_start
       end
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index c327c244f0d51688a2c1e31b8022ddab02d5ab4e..64ea6dd42eb8db8a88d90cd045fdd607c14c93d7 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -27,6 +27,7 @@ module Projects
     def transfer(project, new_namespace)
       Project.transaction do
         old_path = project.path_with_namespace
+        old_namespace = project.namespace
         new_path = File.join(new_namespace.try(:path) || '', project.path)
 
         if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
@@ -51,6 +52,9 @@ module Projects
         # clear project cached events
         project.reset_events_cache
 
+        # Move uploads
+        Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
+
         true
       end
     end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 8253c1f780d58ede386bb38623727d9a7bcccfc6..708c2f00486aef8ca0d54aacee44fcc0d166beab 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -168,6 +168,31 @@ class SystemNoteService
     create_note(noteable: noteable, project: project, author: author, note: body)
   end
 
+  # Called when a branch in Noteable is added or deleted
+  #
+  # noteable    - Noteable object
+  # project     - Project owning noteable
+  # author      - User performing the change
+  # branch_type - :source or :target
+  # branch      - branch name
+  # presence    - :add or :delete
+  #
+  # Example Note text:
+  #
+  #   "Restored target branch `feature`"
+  #
+  # Returns the created Note object
+  def self.change_branch_presence(noteable, project, author, branch_type, branch, presence)
+    verb =
+      if presence == :add
+        'restored'
+      else
+        'deleted'
+      end
+    body = "#{verb} #{branch_type.to_s} branch `#{branch}`".capitalize
+    create_note(noteable: noteable, project: project, author: author, note: body)
+  end
+
   # Called when a Mentionable references a Noteable
   #
   # noteable  - Noteable object being referenced
@@ -302,7 +327,7 @@ class SystemNoteService
     commit_ids = if count == 1
                    existing_commits.first.short_id
                  else
-                   if oldrev
+                   if oldrev && !Gitlab::Git.blank_ref?(oldrev)
                      "#{Commit.truncate_sha(oldrev)}...#{existing_commits.last.short_id}"
                    else
                      "#{existing_commits.first.short_id}..#{existing_commits.last.short_id}"
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index f9673abbfe8a5d0f1ea0d4b6a176c2be373a4128..e82115858342f4af270fa2d474753a52ed2f2829 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -26,7 +26,7 @@ class FileUploader < CarrierWave::Uploader::Base
   end
 
   def secure_url
-    File.join(Gitlab.config.gitlab.url, @project.path_with_namespace, "uploads", @secret, file.filename)
+    File.join("/uploads", @secret, file.filename)
   end
 
   def file_storage?
diff --git a/app/views/abuse_report_mailer/notify.html.haml b/app/views/abuse_report_mailer/notify.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..619533e09a70eea8adfb5e3ab545cdf394dbc6ca
--- /dev/null
+++ b/app/views/abuse_report_mailer/notify.html.haml
@@ -0,0 +1,11 @@
+%p
+  #{link_to @abuse_report.user.name, user_url(@abuse_report.user)} 
+  (@#{@abuse_report.user.username}) was reported for abuse by 
+  #{link_to @abuse_report.reporter.name, user_url(@abuse_report.reporter)} 
+  (@#{@abuse_report.reporter.username}).
+
+%blockquote
+  = @abuse_report.message
+
+%p
+  = link_to "View details", abuse_reports_url
diff --git a/app/views/abuse_report_mailer/notify.text.haml b/app/views/abuse_report_mailer/notify.text.haml
new file mode 100644
index 0000000000000000000000000000000000000000..7dacf857035ba1bd5db53ec4395fb7019178d8ab
--- /dev/null
+++ b/app/views/abuse_report_mailer/notify.text.haml
@@ -0,0 +1,5 @@
+#{@abuse_report.user.name} (@#{@abuse_report.user.username}) was reported for abuse by #{@abuse_report.reporter.name} (@#{@abuse_report.reporter.username}).
+\
+> #{@abuse_report.message}
+\
+View details: #{admin_abuse_reports_url}
diff --git a/app/views/admin/abuse_reports/index.html.haml b/app/views/admin/abuse_reports/index.html.haml
index 2e8746146d15f8b8690b53a6086c602bb8f43ee1..40a5fe4628bf4939812102ee07e0423ab17c1e60 100644
--- a/app/views/admin/abuse_reports/index.html.haml
+++ b/app/views/admin/abuse_reports/index.html.haml
@@ -2,16 +2,17 @@
 %h3.page-title Abuse Reports
 %hr
 - if @abuse_reports.present?
-  %table.table
-    %thead
-      %tr
-        %th Reported by
-        %th Reported at
-        %th Message
-        %th User
-        %th Primary action
-        %th
-    = render @abuse_reports
+  .table-holder
+    %table.table
+      %thead
+        %tr
+          %th Reported by
+          %th Reported at
+          %th Message
+          %th User
+          %th Primary action
+          %th
+      = render @abuse_reports
   = paginate @abuse_reports
 - else
   %h4 There are no abuse reports
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index a36ae0b766c5b2988f96ceba9b58470fb97ae369..7253218c2e93e5741b07bedad7d304a1c621b2fd 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -47,6 +47,12 @@
           = f.label :version_check_enabled do
             = f.check_box :version_check_enabled
             Version check enabled
+    .form-group
+      = f.label :admin_notification_email, class: 'control-label col-sm-2'
+      .col-sm-10
+        = f.text_field :admin_notification_email, class: 'form-control'
+        .help-block
+          Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.
 
   %fieldset
     %legend Account and Limit Settings
@@ -124,5 +130,14 @@
         = f.text_area :help_page_text, class: 'form-control', rows: 4
         .help-block Markdown enabled
 
+  %fieldset
+    %legend Continuous Integration
+    .form-group
+      .col-sm-offset-2.col-sm-10
+        .checkbox
+          = f.label :shared_runners_enabled do
+            = f.check_box :shared_runners_enabled
+            Enable shared runners for a new projects
+
   .form-actions
     = f.submit 'Save', class: 'btn btn-primary'
diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml
index 0ea2ffeda9976b5909d9631b0f397dc1b4b5c9cb..3eb9d61972b314a3fea5e12e7f460b69fbadf933 100644
--- a/app/views/admin/applications/show.html.haml
+++ b/app/views/admin/applications/show.html.haml
@@ -3,25 +3,26 @@
   Application: #{@application.name}
 
 
-%table.table
-  %tr
-    %td
-      Application Id
-    %td
-      %code#application_id= @application.uid
-  %tr
-    %td
-      Secret:
-    %td
-      %code#secret= @application.secret
+.table-holder
+  %table.table
+    %tr
+      %td
+        Application Id
+      %td
+        %code#application_id= @application.uid
+    %tr
+      %td
+        Secret:
+      %td
+        %code#secret= @application.secret
 
-  %tr
-    %td
-      Callback url
-    %td
-      - @application.redirect_uri.split.each do |uri|
-        %div
-          %span.monospace= uri
+    %tr
+      %td
+        Callback url
+      %td
+        - @application.redirect_uri.split.each do |uri|
+          %div
+            %span.monospace= uri
 .form-actions
   = link_to 'Edit', edit_admin_application_path(@application), class: 'btn btn-primary wide pull-left'
   = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index 3a01e1151098e8bc3d553ae2e6e9f68a74f428b5..de5bc050cf0b409dc8ce203efd3a206a198bc369 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -12,24 +12,25 @@
         %i.fa.fa-exclamation-triangle
         There are no running sidekiq processes. Please restart GitLab
     - else
-      %table.table
-        %thead
-          %th USER
-          %th PID
-          %th CPU
-          %th MEM
-          %th STATE
-          %th START
-          %th COMMAND
-        %tbody
-          - @sidekiq_processes.each do |process|
-            - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
-            - data = process.strip.split(' ')
-            %tr
-              %td= gitlab_config.user
-              - 5.times do
-                %td= data.shift
-              %td= data.join(' ')
+      .table-holder
+        %table.table
+          %thead
+            %th USER
+            %th PID
+            %th CPU
+            %th MEM
+            %th STATE
+            %th START
+            %th COMMAND
+          %tbody
+            - @sidekiq_processes.each do |process|
+              - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/)
+              - data = process.strip.split(' ')
+              %tr
+                %td= gitlab_config.user
+                - 5.times do
+                  %td= data.shift
+                %td= data.join(' ')
 
       .clearfix
         %p
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 2bf1689cbc65ab88a6cc0565c1791e23914fdd34..841e6971fb2dd84c27a8c4cd85faeff71b1d60d7 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -5,22 +5,23 @@
     .panel-head-actions
       = link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm"
   - if @deploy_keys.any?
-    %table.table
-      %thead.panel-heading
-        %tr
-          %th Title
-          %th Fingerprint
-          %th Added at
-          %th
-      %tbody
-        - @deploy_keys.each do |deploy_key|
+    .table-holder
+      %table.table
+        %thead.panel-heading
           %tr
-            %td
-              %strong= deploy_key.title
-            %td
-              %code.key-fingerprint= deploy_key.fingerprint
-            %td
-              %span.cgray
-                added #{time_ago_with_tooltip(deploy_key.created_at)}
-            %td
-              = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right"
+            %th Title
+            %th Fingerprint
+            %th Added at
+            %th
+        %tbody
+          - @deploy_keys.each do |deploy_key|
+            %tr
+              %td
+                %strong= deploy_key.title
+              %td
+                %code.key-fingerprint= deploy_key.fingerprint
+              %td
+                %span.cgray
+                  added #{time_ago_with_tooltip(deploy_key.created_at)}
+              %td
+                = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right"
diff --git a/app/views/admin/identities/index.html.haml b/app/views/admin/identities/index.html.haml
index ae57e3adc4d412271f1ac33b2abfc7f7b359035d..8358a14445b24c20eaa0e7edc661f42fd1187c32 100644
--- a/app/views/admin/identities/index.html.haml
+++ b/app/views/admin/identities/index.html.haml
@@ -2,12 +2,13 @@
 = render 'admin/users/head'
 
 - if @identities.present?
-  %table.table
-    %thead
-      %tr
-        %th Provider
-        %th Identifier
-        %th
-    = render @identities
+  .table-holder
+    %table.table
+      %thead
+        %tr
+          %th Provider
+          %th Identifier
+          %th
+      = render @identities
 - else
   %h4 This user has no identities
diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml
index ad58a3837f63b0ea784a58e44a55e815f50de71e..a5ace4e7a3bd2584f1a84d50140bf7289f246eb0 100644
--- a/app/views/admin/labels/_form.html.haml
+++ b/app/views/admin/labels/_form.html.haml
@@ -31,5 +31,5 @@
     = f.submit 'Save', class: 'btn btn-save js-save-button'
     = link_to "Cancel", admin_labels_path, class: 'btn btn-cancel'
 
-:coffeescript
-  new Labels
+:javascript
+  new Labels();
diff --git a/app/views/admin/services/index.html.haml b/app/views/admin/services/index.html.haml
index e2377291142f1273697a258e0e350e621f003543..6a5986f496a9c4b49e5e76e7bf6669dc0cbfe16c 100644
--- a/app/views/admin/services/index.html.haml
+++ b/app/views/admin/services/index.html.haml
@@ -2,22 +2,23 @@
 %h3.page-title Service templates
 %p.light Service template allows you to set default values for project services
 
-%table.table
-  %thead
-    %tr
-      %th
-      %th Service
-      %th Description
-      %th Last edit
-  - @services.sort_by(&:title).each do |service|
-    %tr
-      %td
-        = icon("copy", class: 'clgray')
-      %td
-        = link_to edit_admin_application_settings_service_path(service.id) do
-          %strong= service.title
-      %td
-        = service.description
-      %td.light
-        = time_ago_in_words service.updated_at
-        ago
+.table-holder
+  %table.table
+    %thead
+      %tr
+        %th
+        %th Service
+        %th Description
+        %th Last edit
+    - @services.sort_by(&:title).each do |service|
+      %tr
+        %td
+          = icon("copy", class: 'clgray')
+        %td
+          = link_to edit_admin_application_settings_service_path(service.id) do
+            %strong= service.title
+        %td
+          = service.description
+        %td.light
+          = time_ago_in_words service.updated_at
+          ago
diff --git a/app/views/users/_profile.html.haml b/app/views/admin/users/_profile.html.haml
similarity index 100%
rename from app/views/users/_profile.html.haml
rename to app/views/admin/users/_profile.html.haml
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index a383ea573841a5fbcfc9f8fa2d4038f4b2c7ecda..0848504b7a638939cb48f3470374afbfa36099ce 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -8,13 +8,13 @@
         = @user.name
       %ul.well-list
         %li
-          = image_tag avatar_icon(@user.email, 60), class: "avatar s60"
+          = image_tag avatar_icon(@user, 60), class: "avatar s60"
         %li
           %span.light Profile page:
           %strong
             = link_to user_path(@user) do
               = @user.username
-    = render 'users/profile', user: @user
+    = render 'admin/users/profile', user: @user
 
     .panel.panel-default
       .panel-heading
diff --git a/app/views/ci/admin/events/index.html.haml b/app/views/ci/admin/events/index.html.haml
index f9ab09943046b9327c1d4594b2b67e471c841fe3..5a5b4dc7c3571cd2cee561369e3cf0c7ae01b87c 100644
--- a/app/views/ci/admin/events/index.html.haml
+++ b/app/views/ci/admin/events/index.html.haml
@@ -1,17 +1,18 @@
-%table.table
-  %thead
-    %tr
-      %th User ID
-      %th Description
-      %th When
-  - @events.each do |event|
-    %tr
-      %td
-        = event.user_id
-      %td
-        = event.description
-      %td.light
-        = time_ago_in_words event.updated_at
-        ago
+.table-holder
+  %table.table
+    %thead
+      %tr
+        %th User ID
+        %th Description
+        %th When
+    - @events.each do |event|
+      %tr
+        %td
+          = event.user_id
+        %td
+          = event.description
+        %td.light
+          = time_ago_in_words event.updated_at
+          ago
 
-= paginate @events
\ No newline at end of file
+= paginate @events
diff --git a/app/views/ci/admin/projects/index.html.haml b/app/views/ci/admin/projects/index.html.haml
index dc7b041473be1c872521d055e525be4f0e5f3f02..0da8547924bb8c41244245eabbcc0288e14c681a 100644
--- a/app/views/ci/admin/projects/index.html.haml
+++ b/app/views/ci/admin/projects/index.html.haml
@@ -1,15 +1,16 @@
-%table.table
-  %thead
-    %tr
-      %th ID
-      %th Name
-      %th Last build
-      %th Access
-      %th Builds
-      %th
+.table-holder
+  %table.table
+    %thead
+      %tr
+        %th ID
+        %th Name
+        %th Last build
+        %th Access
+        %th Builds
+        %th
 
-  - @projects.each do |project|
-    = render "ci/admin/projects/project", project: project
+    - @projects.each do |project|
+      = render "ci/admin/projects/project", project: project
 
 = paginate @projects
 
diff --git a/app/views/ci/admin/runner_projects/index.html.haml b/app/views/ci/admin/runner_projects/index.html.haml
index f049b4f4c4e483a9937dfc414a7494d7ceaaa81c..6b4e3b2cb386bf2dab3b200b9a5f6779191746ee 100644
--- a/app/views/ci/admin/runner_projects/index.html.haml
+++ b/app/views/ci/admin/runner_projects/index.html.haml
@@ -1,5 +1,5 @@
 %p.lead
-  To register new runner visit #{link_to 'this page ', ci_runners_path}
+  To register a new runner visit #{link_to 'this page ', ci_runners_path}
 
 .row
   .col-md-8
diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml
index 01ce81b44764d8dd4e45b3aea5209d25cd0e50b4..bacaccfbffa117ef7bbfab002e6c24183ad2daf4 100644
--- a/app/views/ci/admin/runners/index.html.haml
+++ b/app/views/ci/admin/runners/index.html.haml
@@ -1,5 +1,5 @@
 %p.lead
-  %span To register new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication.
+  %span To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication.
   %code #{GitlabCi::REGISTRATION_TOKEN}
 
 .bs-callout
@@ -21,7 +21,7 @@
         \- run builds from assigned projects
       %li
         %span.label.label-danger paused
-        \- runner will not receive any new build
+        \- runner will not receive any new builds
 
 .append-bottom-20.clearfix
   .pull-left
@@ -35,18 +35,19 @@
 
 %br
 
-%table.table
-  %thead
-    %tr
-      %th Type
-      %th Runner token
-      %th Description
-      %th Projects
-      %th Builds
-      %th Tags
-      %th Last contact
-      %th
+.table-holder
+  %table.table
+    %thead
+      %tr
+        %th Type
+        %th Runner token
+        %th Description
+        %th Projects
+        %th Builds
+        %th Tags
+        %th Last contact
+        %th
 
-  - @runners.each do |runner|
-    = render "ci/admin/runners/runner", runner: runner
+    - @runners.each do |runner|
+      = render "ci/admin/runners/runner", runner: runner
 = paginate @runners
diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml
index 92787b2e6ac933b0d7dae3f9e2388dde4e068f41..1498db46a80ac044fed4a4b2f8c5d9a06e890d25 100644
--- a/app/views/ci/admin/runners/show.html.haml
+++ b/app/views/ci/admin/runners/show.html.haml
@@ -13,13 +13,13 @@
 
 - if @runner.shared?
   .bs-callout.bs-callout-success
-    %h4 This runner will process build from ALL UNASSIGNED projects
+    %h4 This runner will process builds from ALL UNASSIGNED projects
     %p
       If you want runners to build only specific projects, enable them in the table below.
       Keep in mind that this is a one way transition.
 - else
   .bs-callout.bs-callout-info
-    %h4 This runner will process build only from ASSIGNED projects
+    %h4 This runner will process builds only from ASSIGNED projects
     %p You can't make this a shared runner.
 %hr
 = form_for @runner, url: ci_admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f|
@@ -53,13 +53,14 @@
             %th
         - @runner.runner_projects.each do |runner_project|
           - project = runner_project.project
-          %tr.alert-info
-            %td
-              %strong
-                = project.name
-            %td
-              .pull-right
-                = link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
+          - if project.gl_project
+            %tr.alert-info
+              %td
+                %strong
+                  = project.name
+              %td
+                .pull-right
+                  = link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
 
     %table.table
       %thead
@@ -103,21 +104,26 @@
           %th Finished at
 
       - @builds.each do |build|
+        - gl_project = build.gl_project
         %tr.build
           %td.id
-            - gl_project = build.project.gl_project
-            = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
+            - if gl_project
+              = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
+                = build.id
+            - else
               = build.id
 
           %td.status
             = ci_status_with_icon(build.status)
 
           %td.status
-            = build.project.name
+            - if gl_project
+              = gl_project.name_with_namespace
 
           %td.build-link
-            = link_to ci_status_path(build.commit) do
-              %strong #{build.commit.short_sha}
+            - if gl_project
+              = link_to ci_status_path(build.commit) do
+                %strong #{build.commit.short_sha}
 
           %td.timestamp
             - if build.finished_at
diff --git a/app/views/ci/events/index.html.haml b/app/views/ci/events/index.html.haml
deleted file mode 100644
index 779f49b3d3a761c50f7e62e925f62087e494ca7f..0000000000000000000000000000000000000000
--- a/app/views/ci/events/index.html.haml
+++ /dev/null
@@ -1,19 +0,0 @@
-%h3.page-title Events
-
-%table.table
-  %thead
-    %tr
-      %th User ID
-      %th Description
-      %th When
-  - @events.each do |event|
-    %tr
-      %td
-        = event.user_id
-      %td
-        = event.description
-      %td.light
-        = time_ago_in_words event.updated_at
-        ago
-
-= paginate @events
\ No newline at end of file
diff --git a/app/views/ci/lints/_create.html.haml b/app/views/ci/lints/_create.html.haml
index e2179e60f3ec886029ff5bac0cd8a8096148af29..77f78caa8d886ac22324b676429399862314e8e3 100644
--- a/app/views/ci/lints/_create.html.haml
+++ b/app/views/ci/lints/_create.html.haml
@@ -4,29 +4,35 @@
     syntax is correct
     %i.fa.fa-ok.correct-syntax
 
-  %table.table.table-bordered
-    %thead
-      %tr
-        %th Parameter
-        %th Value
-    %tbody
-      - @stages.each do |stage|
-        - @builds.select { |build| build[:stage] == stage }.each do |build|
-          %tr
-            %td #{stage.capitalize} Job - #{build[:name]}
-            %td
-              %pre
-                = simple_format build[:script]
+  .table-holder
+    %table.table.table-bordered
+      %thead
+        %tr
+          %th Parameter
+          %th Value
+      %tbody
+        - @stages.each do |stage|
+          - @builds.select { |build| build[:stage] == stage }.each do |build|
+            %tr
+              %td #{stage.capitalize} Job - #{build[:name]}
+              %td
+                %pre
+                  = simple_format build[:commands]
 
-              %br
-              %b Tag list:
-              = build[:tags]
-              %br
-              %b Refs only:
-              = build[:only] && build[:only].join(", ")
-              %br
-              %b Refs except:
-              = build[:except] && build[:except].join(", ")
+                %br
+                %b Tag list:
+                = build[:tags]
+                %br
+                %b Refs only:
+                = build[:only] && build[:only].join(", ")
+                %br
+                %b Refs except:
+                = build[:except] && build[:except].join(", ")
+                %br
+                %b When:
+                = build[:when]
+                - if build[:allow_failure]
+                  %b Allowed to fail
 
 -else
   %p
diff --git a/app/views/ci/lints/show.html.haml b/app/views/ci/lints/show.html.haml
index a9b954771c5c0770a92f31065b4358cb45f8fb74..fb9057e4882069229f557c83af711e1dbc719ab3 100644
--- a/app/views/ci/lints/show.html.haml
+++ b/app/views/ci/lints/show.html.haml
@@ -11,15 +11,17 @@
     .controls.pull-left.prepend-top-10
       = submit_tag "Validate", class: 'btn btn-success submit-yml'
 
-    
+
 %p.text-center.loading
   %i.fa.fa-refresh.fa-spin
 
 .results.prepend-top-20
 
-:coffeescript
-  $(".loading").hide()
-  $('form').bind 'ajax:beforeSend', ->
-    $(".loading").show()
-  $('form').bind 'ajax:complete', ->
-    $(".loading").hide()
+:javascript
+  $(".loading").hide();
+  $('form').bind('ajax:beforeSend', function() {
+    $(".loading").show();
+  });
+  $('form').bind('ajax:complete', function() {
+    $(".loading").hide();
+  });
diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml
index 69689a75022fcb6146f40d45ec0d773de49aa207..b0aaea89075b1e29ce5a707e40e294c667d8d3b3 100644
--- a/app/views/ci/notify/build_fail_email.html.haml
+++ b/app/views/ci/notify/build_fail_email.html.haml
@@ -7,13 +7,17 @@
     = @project.name
 
 %p
-  Commit link: #{gitlab_commit_link(@project, @build.commit.short_sha)}
+  Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)}
 %p
   Author: #{@build.commit.git_author_name}
 %p
   Branch: #{@build.ref}
+%p
+  Stage: #{@build.stage}
+%p
+  Job: #{@build.name}
 %p
   Message: #{@build.commit.git_commit_message}
 
 %p
-  Url: #{link_to @build.short_sha, namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)}
+  Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)}
diff --git a/app/views/ci/notify/build_fail_email.text.erb b/app/views/ci/notify/build_fail_email.text.erb
index 6de5dc10f179184ffdbf0591ff0380a42739ae0e..17a3b9b1d33df14cd27f07d35f9876323f57c81d 100644
--- a/app/views/ci/notify/build_fail_email.text.erb
+++ b/app/views/ci/notify/build_fail_email.text.erb
@@ -4,6 +4,8 @@ Status:   <%= @build.status %>
 Commit:   <%= @build.commit.short_sha %>
 Author:   <%= @build.commit.git_author_name %>
 Branch:   <%= @build.ref %>
+Stage:    <%= @build.stage %>
+Job:      <%= @build.name %>
 Message:  <%= @build.commit.git_commit_message %>
 
 Url:      <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %>
diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml
index 4e3015a356bea801bea8087a7fe051445167b8be..24c439e50eb39fa3a0a79fbaebe8ba8bf3b2dc03 100644
--- a/app/views/ci/notify/build_success_email.html.haml
+++ b/app/views/ci/notify/build_success_email.html.haml
@@ -8,13 +8,17 @@
     = @project.name
 
 %p
-  Commit link: #{gitlab_commit_link(@project, @build.commit.short_sha)}
+  Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)}
 %p
   Author: #{@build.commit.git_author_name}
 %p
   Branch: #{@build.ref}
+%p
+  Stage: #{@build.stage}
+%p
+  Job: #{@build.name}
 %p
   Message: #{@build.commit.git_commit_message}
 
 %p
-  Url: #{link_to @build.short_sha, namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)}
+  Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)}
diff --git a/app/views/ci/notify/build_success_email.text.erb b/app/views/ci/notify/build_success_email.text.erb
index d0a43ae1c12915fd47fd85b6d0b14eb25a9b1c70..bc8b978c3d75b8a2a03acb3bf152e84ce5e18888 100644
--- a/app/views/ci/notify/build_success_email.text.erb
+++ b/app/views/ci/notify/build_success_email.text.erb
@@ -4,6 +4,8 @@ Status:   <%= @build.status %>
 Commit:   <%= @build.commit.short_sha %>
 Author:   <%= @build.commit.git_author_name %>
 Branch:   <%= @build.ref %>
+Stage:    <%= @build.stage %>
+Job:      <%= @build.name %>
 Message:  <%= @build.commit.git_commit_message %>
 
 Url:      <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %>
diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..9c2290bc4a5cf778865e6c800eece3ab78b7de14
--- /dev/null
+++ b/app/views/ci/projects/index.html.haml
@@ -0,0 +1,20 @@
+.wiki
+  %h1
+    GitLab CI is now integrated in GitLab UI
+  %h2 For existing projects
+
+  %p
+    Check the following pages to find the CI status you're looking for:
+
+  %ul
+    %li Projects page - shows CI status for each project.
+    %li Project commits page - show CI status for each commit.
+
+
+
+  %h2 For new projects
+
+  %p
+    If you want to enable CI for a new project it is easy as adding
+    = link_to ".gitlab-ci.yml", "http://doc.gitlab.com/ce/ci/yaml/README.html"
+    file to your repository
diff --git a/app/views/ci/user_sessions/new.html.haml b/app/views/ci/user_sessions/new.html.haml
index 308b217ea78f1db6171d47255e02ff3b82505ed4..b8d9a1d7089aca03e522be413f4c439510a87c58 100644
--- a/app/views/ci/user_sessions/new.html.haml
+++ b/app/views/ci/user_sessions/new.html.haml
@@ -1,8 +1,7 @@
 .login-block
   %h2 Login using GitLab account
   %p.light
-    Make sure you have account on GitLab server
+    Make sure you have an account on the GitLab server
     = link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink
   %hr
   = link_to "Login with GitLab", auth_ci_user_sessions_path(state: params[:state]), no_turbolink.merge( class: 'btn btn-login btn-success' )
-
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index c249f5cacecb8b988b549acc08dd5875015e99a7..f3f3f58111e3cf8d54bdd4e3b90ed6d74e405679 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -16,4 +16,4 @@
     - group = group_member.group
     = render 'shared/groups/group', group: group, group_member: group_member
 
-= paginate @group_members
+= paginate @group_members, theme: 'gitlab'
diff --git a/app/views/dashboard/milestones/_issue.html.haml b/app/views/dashboard/milestones/_issue.html.haml
index f689b9698eb76806e1f50a6fdaa1e1eb9e9058bd..1408ebdd5dcf6953a63ae06a96ab531118871524 100644
--- a/app/views/dashboard/milestones/_issue.html.haml
+++ b/app/views/dashboard/milestones/_issue.html.haml
@@ -7,4 +7,4 @@
     = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title
   .pull-right.assignee-icon
     - if issue.assignee
-      = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16"
+      = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16"
diff --git a/app/views/dashboard/milestones/_merge_request.html.haml b/app/views/dashboard/milestones/_merge_request.html.haml
index 8f5c4cce529b601421a4cb813422e6b86cb99a87..77c46de030b1b3bd56121581f9457828a9e68588 100644
--- a/app/views/dashboard/milestones/_merge_request.html.haml
+++ b/app/views/dashboard/milestones/_merge_request.html.haml
@@ -7,4 +7,4 @@
     = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title
   .pull-right.assignee-icon
     - if merge_request.assignee
-      = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16"
+      = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16"
diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml
index 0d204ced7ea339ac57d4f88aad22d19713f832b9..2fe14c6388c3255b6e014400d45ddbb1f75d65ad 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -13,26 +13,28 @@
     %span All issues for this milestone are closed. You may close the milestone now.
 
 .description
-%table.table
-  %thead
-    %tr
-      %th Project
-      %th Open issues
-      %th State
-      %th Due date
-  - @dashboard_milestone.milestones.each do |milestone|
-    %tr
-      %td
-        = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
-      %td
-        = milestone.issues.opened.count
-      %td
-        - if milestone.closed?
-          Closed
-        - else
-          Open
-      %td
-        = milestone.expires_at
+
+.table-holder
+  %table.table
+    %thead
+      %tr
+        %th Project
+        %th Open issues
+        %th State
+        %th Due date
+    - @dashboard_milestone.milestones.each do |milestone|
+      %tr
+        %td
+          = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
+        %td
+          = milestone.issues.opened.count
+        %td
+          - if milestone.closed?
+            Closed
+          - else
+            Open
+        %td
+          = milestone.expires_at
 
 .context
   %p.lead
@@ -79,7 +81,7 @@
       - @dashboard_milestone.participants.each do |user|
         %li
           = link_to user, title: user.name, class: "darken" do
-            = image_tag avatar_icon(user.email, 32), class: "avatar s32"
+            = image_tag avatar_icon(user, 32), class: "avatar s32"
             %strong= truncate(user.name, lenght: 40)
             %br
             %small.cgray= user.username
diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml
index d3908062f43e5cdccb0f57694f98fcf187aaa1b4..07b6d57932ea70a3bef91bcc2a52131ed8899812 100644
--- a/app/views/dashboard/snippets/index.html.haml
+++ b/app/views/dashboard/snippets/index.html.haml
@@ -6,33 +6,29 @@
 .gray-content-block
   .pull-right
     = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
-      Add new snippet
+      = icon('plus')
+      New Snippet
 
-  .oneline
-    Share code pastes with others out of git repository
-
-%ul.nav.nav-tabs.prepend-top-20
-  = nav_tab :scope, nil do
-    = link_to dashboard_snippets_path do
+  .btn-group.btn-group-next.snippet-scope-menu
+    = link_to dashboard_snippets_path, class: "btn btn-default #{"active" unless params[:scope]}" do
       All
       %span.badge
         = current_user.snippets.count
-  = nav_tab :scope, 'are_private' do
-    = link_to dashboard_snippets_path(scope: 'are_private') do
+
+    = link_to dashboard_snippets_path(scope: 'are_private'), class: "btn btn-default #{"active" if params[:scope] == "are_private"}" do
       Private
       %span.badge
         = current_user.snippets.are_private.count
-  = nav_tab :scope, 'are_internal' do
-    = link_to dashboard_snippets_path(scope: 'are_internal') do
+
+    = link_to dashboard_snippets_path(scope: 'are_internal'), class: "btn btn-default #{"active" if params[:scope] == "are_internal"}" do
       Internal
       %span.badge
         = current_user.snippets.are_internal.count
-  = nav_tab :scope, 'are_public' do
-    = link_to dashboard_snippets_path(scope: 'are_public') do
+
+    = link_to dashboard_snippets_path(scope: 'are_public'), class: "btn btn-default #{"active" if params[:scope] == "are_public"}" do
       Public
       %span.badge
         = current_user.snippets.are_public.count
 
-.my-snippets
-  = render 'snippets/snippets'
+= render 'snippets/snippets'
 
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index 41ad2c231d4d1cfaa0c2d3478d2e185ca579dcbb..9a1331b2549361fbb5b1ed389b6e702bdc4a761e 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -7,26 +7,34 @@
       %h3 Sign in
   .login-body
     - if form_based_providers.any?
-      %ul.nav.nav-tabs
+      - if form_based_providers.count >= 2 || signin_enabled?
+        %ul.nav.nav-tabs
+          - if crowd_enabled?
+            %li.active
+              = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
+          - @ldap_servers.each_with_index do |server, i|
+            %li{class: (:active if i.zero? && !crowd_enabled?)}
+              = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
+          - if signin_enabled?
+            %li
+              = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
+        .tab-content
+          - if crowd_enabled?
+            %div.tab-pane.active{id: "tab-crowd"}
+              = render 'devise/sessions/new_crowd'
+          - @ldap_servers.each_with_index do |server, i|
+            %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)}
+              = render 'devise/sessions/new_ldap', server: server
+          - if signin_enabled?
+            %div#tab-signin.tab-pane
+              = render 'devise/sessions/new_base'
+      - else
         - if crowd_enabled?
-          %li.active
-            = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
-        - @ldap_servers.each_with_index do |server, i|
-          %li{class: (:active if i.zero? && !crowd_enabled?)}
-            = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
-        - if signin_enabled?
-          %li
-            = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
-      .tab-content
-        - if crowd_enabled?
-          %div.tab-pane.active{id: "tab-crowd"}
-            = render 'devise/sessions/new_crowd'
-        - @ldap_servers.each_with_index do |server, i|
-          %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)}
-            = render 'devise/sessions/new_ldap', server: server
-        - if signin_enabled?
-          %div#tab-signin.tab-pane
-            = render 'devise/sessions/new_base'
+          = render 'devise/sessions/new_crowd'
+        - elsif @ldap_servers.any?
+          = render 'devise/sessions/new_ldap', server: @ldap_servers.first
+        - elsif signin_enabled?
+          = render 'devise/sessions/new_base'
 
     - elsif signin_enabled?
       = render 'devise/sessions/new_base'
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index 3b0b19107ca53612bb61fced036d43bea48b1b4f..ba4c5b86efbcce2dc13a69d6f9ea3fce75b4895b 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -1,17 +1,19 @@
 - 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
-  %thead
-    %tr
-      %th Name
-      %th Callback URL
-      %th
-      %th
-  %tbody
-    - @applications.each do |application|
-      %tr{:id => "application_#{application.id}"}
-        %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
+
+.table-holder
+  %table.table.table-striped
+    %thead
+      %tr
+        %th Name
+        %th Callback URL
+        %th
+        %th
+    %tbody
+      - @applications.each do |application|
+        %tr{:id => "application_#{application.id}"}
+          %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
diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml
index 80340aca54cecfd832202359055c59ad7821a680..47442b78d48e4972b4f87e59070e83502ac4a7ea 100644
--- a/app/views/doorkeeper/applications/show.html.haml
+++ b/app/views/doorkeeper/applications/show.html.haml
@@ -2,26 +2,26 @@
 %h3.page-title
   Application: #{@application.name}
 
+.table-holder
+  %table.table
+    %tr
+      %td
+        Application Id
+      %td
+        %code#application_id= @application.uid
+    %tr
+      %td
+        Secret:
+      %td
+        %code#secret= @application.secret
 
-%table.table
-  %tr
-    %td
-      Application Id
-    %td
-      %code#application_id= @application.uid
-  %tr
-    %td
-      Secret:
-    %td
-      %code#secret= @application.secret
-
-  %tr
-    %td
-      Callback url
-    %td
-      - @application.redirect_uri.split.each do |uri|
-        %div
-          %span.monospace= uri
+    %tr
+      %td
+        Callback url
+      %td
+        - @application.redirect_uri.split.each do |uri|
+          %div
+            %span.monospace= uri
 .form-actions
   = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left'
   = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml
index 814cdc987ef7c466f4dbccab541d16be2845947b..b184b9c01d4e8c8c4ab1b07efbcb611c887ba740 100644
--- a/app/views/doorkeeper/authorized_applications/index.html.haml
+++ b/app/views/doorkeeper/authorized_applications/index.html.haml
@@ -1,16 +1,17 @@
 %header.page-header
   %h1 Your authorized applications
 %main{:role => "main"}
-  %table.table.table-striped
-    %thead
-      %tr
-        %th Application
-        %th Created At
-        %th
-        %th
-    %tbody
-      - @applications.each do |application|
+  .table-holder
+    %table.table.table-striped
+      %thead
         %tr
-          %td= application.name
-          %td= application.created_at.strftime('%Y-%m-%d %H:%M:%S')
-          %td= render 'delete_form', application: application
\ No newline at end of file
+          %th Application
+          %th Created At
+          %th
+          %th
+      %tbody
+        - @applications.each do |application|
+          %tr
+            %td= application.name
+            %td= application.created_at.strftime('%Y-%m-%d %H:%M:%S')
+            %td= render 'delete_form', application: application
diff --git a/app/views/events/_event_issue.atom.haml b/app/views/events/_event_issue.atom.haml
index 4259f64c19102a11f7543903e53ce20e1ec6ef9f..fad65310021542a8603f16bed8f320960fa2b2af 100644
--- a/app/views/events/_event_issue.atom.haml
+++ b/app/views/events/_event_issue.atom.haml
@@ -1,3 +1,2 @@
 %div{xmlns: "http://www.w3.org/1999/xhtml"}
-  - if issue.description.present?
-    = markdown(issue.description, xhtml: true, reference_only_path: false, project: issue.project)
+  = markdown(issue.description, pipeline: :atom, project: issue.project)
diff --git a/app/views/events/_event_merge_request.atom.haml b/app/views/events/_event_merge_request.atom.haml
index e8ed13df783952f4a0b10d3ecd73b86c915eabab..19bdc7b9ca540f9de4607f90e64901f55aac4411 100644
--- a/app/views/events/_event_merge_request.atom.haml
+++ b/app/views/events/_event_merge_request.atom.haml
@@ -1,3 +1,2 @@
 %div{xmlns: "http://www.w3.org/1999/xhtml"}
-  - if merge_request.description.present?
-    = markdown(merge_request.description, xhtml: true, reference_only_path: false, project: merge_request.project)
+  = markdown(merge_request.description, pipeline: :atom, project: merge_request.project)
diff --git a/app/views/events/_event_note.atom.haml b/app/views/events/_event_note.atom.haml
index cfbfba502027f1a65cbb892f0955db56ca052a05..b730ebbd5f9f5052b272d89939b0e2f6540580d4 100644
--- a/app/views/events/_event_note.atom.haml
+++ b/app/views/events/_event_note.atom.haml
@@ -1,2 +1,2 @@
 %div{xmlns: "http://www.w3.org/1999/xhtml"}
-  = markdown(note.note, xhtml: true, reference_only_path: false, project: note.project)
+  = markdown(note.note, pipeline: :atom, project: note.project)
diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml
index 3625cb49d8b6b459219fae6edf6d55d80990c93d..b271b9daff1160fba791da0c669b81188ade9f8a 100644
--- a/app/views/events/_event_push.atom.haml
+++ b/app/views/events/_event_push.atom.haml
@@ -6,7 +6,7 @@
       %i
         at
         = commit[:timestamp].to_time.to_s(:short)
-    %blockquote= markdown(escape_once(commit[:message]), xhtml: true, reference_only_path: false, project: event.project)
+    %blockquote= markdown(escape_once(commit[:message]), pipeline: :atom, project: event.project)
   - if event.commits_count > 15
     %p
       %i
diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml
index 7e4fa7d48734cde7c9ab85bf58eb255d0d852b65..0f100c39ffb8f1c5cd42ad9a0411b5efa456d74a 100644
--- a/app/views/explore/snippets/index.html.haml
+++ b/app/views/explore/snippets/index.html.haml
@@ -10,7 +10,8 @@
   - if current_user
     .pull-right
       = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
-        Add new snippet
+        = icon('plus')
+        New Snippet
 
   .oneline
     Public snippets created by you and other users are listed here
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index ae8fc9f85f001b9847e0c367cf08be95c9c6f83b..57308a661c0827ddd86343514254f080c2fbeb8e 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -25,6 +25,15 @@
             %hr
             = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar"
 
+      .form-group
+        %hr
+        = f.label :public, class: 'control-label' do
+          Public
+        .col-sm-10
+          .checkbox
+            = f.check_box :public
+            %span.descr Make this group public (even if there is no any public project inside this group)
+
       .form-actions
         = f.submit 'Save group', class: "btn btn-save"
 
diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml
index b5f359279d579847ac43223569204543958f9f3a..3c19381321aec37e36924f7c34635726ea265fde 100644
--- a/app/views/groups/group_members/_group_member.html.haml
+++ b/app/views/groups/group_members/_group_member.html.haml
@@ -5,7 +5,7 @@
 %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)}
   %span{class: ("list-item-name" if show_controls)}
     - if member.user
-      = image_tag avatar_icon(user.email, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(user, 16), class: "avatar s16", alt: ''
       %strong
         = link_to user.name, user_path(user)
       %span.cgray= user.username
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index fee4b0052b590af4f5c3f62d65717275c7e120eb..15d289471c9a743ac532d4b6b86588d98577971c 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -36,7 +36,8 @@
 
 = paginate @members, theme: 'gitlab'
 
-:coffeescript
-  $('form.member-search-form').on 'submit', (event) ->
-    event.preventDefault()
-    Turbolinks.visit @.action + '?' + $(@).serialize()
+:javascript
+  $('form.member-search-form').on('submit', function(event) {
+    event.preventDefault();
+    Turbolinks.visit(this.action + '?' + $(this).serialize());
+  });
diff --git a/app/views/groups/milestones/_issue.html.haml b/app/views/groups/milestones/_issue.html.haml
index 09f9b4b896906d07db480142df62bbbe6b241174..9b85d83d6d854cc8e0f48ed78bfd39c3b7edfeb9 100644
--- a/app/views/groups/milestones/_issue.html.haml
+++ b/app/views/groups/milestones/_issue.html.haml
@@ -7,4 +7,4 @@
     = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title
   .pull-right.assignee-icon
     - if issue.assignee
-      = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16", alt: ''
diff --git a/app/views/groups/milestones/_merge_request.html.haml b/app/views/groups/milestones/_merge_request.html.haml
index d0d1426762b5fb41840f4004bf5b0582150b3a21..e3aa4aad198f558929077e653dd482339f81fd29 100644
--- a/app/views/groups/milestones/_merge_request.html.haml
+++ b/app/views/groups/milestones/_merge_request.html.haml
@@ -7,4 +7,4 @@
     = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title
   .pull-right.assignee-icon
     - if merge_request.assignee
-      = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: ''
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index 0c213f421869190ad434643947ea71cb005d9baf..a92ad5d751b985815ddcf3dadae39dacb1caea5c 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -21,26 +21,28 @@
     %span All issues for this milestone are closed. You may close the milestone now.
 
 .description
-%table.table
-  %thead
-    %tr
-      %th Project
-      %th Open issues
-      %th State
-      %th Due date
-  - @group_milestone.milestones.each do |milestone|
-    %tr
-      %td
-        = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
-      %td
-        = milestone.issues.opened.count
-      %td
-        - if milestone.closed?
-          Closed
-        - else
-          Open
-      %td
-        = milestone.expires_at
+
+.table-holder
+  %table.table
+    %thead
+      %tr
+        %th Project
+        %th Open issues
+        %th State
+        %th Due date
+    - @group_milestone.milestones.each do |milestone|
+      %tr
+        %td
+          = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
+        %td
+          = milestone.issues.opened.count
+        %td
+          - if milestone.closed?
+            Closed
+          - else
+            Open
+        %td
+          = milestone.expires_at
 
 .context
   %p.lead
@@ -87,7 +89,7 @@
       - @group_milestone.participants.each do |user|
         %li
           = link_to user, title: user.name, class: "darken" do
-            = image_tag avatar_icon(user.email, 32), class: "avatar s32"
+            = image_tag avatar_icon(user, 32), class: "avatar s32"
             %strong= truncate(user.name, lenght: 40)
             %br
             %small.cgray= user.username
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index e809d99ba71ffb20a69dbe30e9e97f05dc508760..7e801b5332d9cc848a98e92f51c8e84db0bed059 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -99,6 +99,12 @@
                   .key c
                 %td
                   Go to commits
+              %tr
+                %td.shortcut
+                  .key g
+                  .key b
+                %td
+                  Go to builds
               %tr
                 %td.shortcut
                   .key g
@@ -216,8 +222,8 @@
 
 
 :javascript
-  $('.js-more-help-button').click(function(e){
-      $(this).remove()
-      $('.hidden-shortcut').show()
-      e.preventDefault()
+  $('.js-more-help-button').click(function (e) {
+    $(this).remove()l
+    $('.hidden-shortcut').show();
+    e.preventDefault();
   });
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 777eb482714b37730689522e973e4f4d1a0ef448..1f09a27e2d6d280b7bf7cc9d5eb1bb76b0f8bb59 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -14,45 +14,46 @@
     = button_tag 'Import all projects', class: "btn btn-success js-import-all"
 
 
-%table.table.import-jobs
-  %thead
-    %tr
-      %th From Bitbucket
-      %th To GitLab
-      %th Status
-  %tbody
-    - @already_added_projects.each do |project|
-      %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
-        %td
-          = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: "_blank"
-        %td
-          %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
-        %td.job-status
-          - if project.import_status == 'finished'
-            %span
-              %i.fa.fa-check
-              done
-          - elsif project.import_status == 'started'
-            %i.fa.fa-spinner.fa-spin
-            started
-          - else
-            = project.human_import_status_name
+.table-holder
+  %table.table.import-jobs
+    %thead
+      %tr
+        %th From Bitbucket
+        %th To GitLab
+        %th Status
+    %tbody
+      - @already_added_projects.each do |project|
+        %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+          %td
+            = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: "_blank"
+          %td
+            %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+          %td.job-status
+            - if project.import_status == 'finished'
+              %span
+                %i.fa.fa-check
+                done
+            - elsif project.import_status == 'started'
+              %i.fa.fa-spinner.fa-spin
+              started
+            - else
+              = project.human_import_status_name
 
-    - @repos.each do |repo|
-      %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"}
-        %td
-          = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank"
-        %td.import-target
-          = "#{repo["owner"]}/#{repo["slug"]}"
-        %td.import-actions.job-status
-          = button_tag "Import", class: "btn js-add-to-import"
-    - @incompatible_repos.each do |repo|
-      %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"}
-        %td
-          = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank"
-        %td.import-target
-        %td.import-actions-job-status
-          = label_tag "Incompatible Project", nil, class: "label label-danger"
+      - @repos.each do |repo|
+        %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"}
+          %td
+            = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank"
+          %td.import-target
+            = "#{repo["owner"]}/#{repo["slug"]}"
+          %td.import-actions.job-status
+            = button_tag "Import", class: "btn js-add-to-import"
+      - @incompatible_repos.each do |repo|
+        %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"}
+          %td
+            = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank"
+          %td.import-target
+          %td.import-actions-job-status
+            = label_tag "Incompatible Project", nil, class: "label label-danger"
 
 - if @incompatible_repos.any?
   %p
@@ -65,5 +66,5 @@
     again.
 
 
-:coffeescript
-  new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}")
+:javascript
+  new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}");
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
index 25cebfb36650b49137f5ce6818d882691afe72ba..bc3c90294e3bcb0afc4e6180bad67a20210843ee 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -22,28 +22,29 @@
       %strong Map a FogBugz account ID to a GitLab user
       %p
         Selecting a GitLab user will add a link to the GitLab user in the descriptions
-        of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also 
+        of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also
         associate and/or assign these issues and comments with the selected user.
 
-  %table.table
-    %thead
-      %tr
-        %th ID
-        %th Name
-        %th Email
-        %th GitLab User
-    %tbody
-      - @user_map.each do |id, user|
+  .table-holder
+    %table.table
+      %thead
         %tr
-          %td= id
-          %td= text_field_tag "users[#{id}][name]", user[:name], class: 'form-control'
-          %td= text_field_tag "users[#{id}][email]", user[:email], class: 'form-control'
-          %td
-            = users_select_tag("users[#{id}][gitlab_user]", class: 'custom-form-control',
-              scope: :all, email_user: true, selected: user[:gitlab_user])
+          %th ID
+          %th Name
+          %th Email
+          %th GitLab User
+      %tbody
+        - @user_map.each do |id, user|
+          %tr
+            %td= id
+            %td= text_field_tag "users[#{id}][name]", user[:name], class: 'form-control'
+            %td= text_field_tag "users[#{id}][email]", user[:email], class: 'form-control'
+            %td
+              = users_select_tag("users[#{id}][gitlab_user]", class: 'custom-form-control',
+                scope: :all, email_user: true, selected: user[:gitlab_user])
 
   .form-actions
     = submit_tag 'Continue to the next step', class: 'btn btn-create'
 
-:coffeescript
-  new UsersSelect()
+:javascript
+  new UsersSelect();
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index f179ece402d1f81c88308e4a4e63b0ed7154d7e5..b902006597bd64239ba9fffa917dd044bbce21e8 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -14,38 +14,39 @@
   %p
   = button_tag 'Import all projects', class: 'btn btn-success js-import-all'
 
-%table.table.import-jobs
-  %thead
-    %tr
-      %th From FogBugz
-      %th To GitLab
-      %th Status
-  %tbody
-    - @already_added_projects.each do |project|
-      %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
-        %td
-          = project.import_source
-        %td
-          %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
-        %td.job-status
-          - if project.import_status == 'finished'
-            %span
-              %i.fa.fa-check
-              done
-          - elsif project.import_status == 'started'
-            %i.fa.fa-spinner.fa-spin
-            started
-          - else
-            = project.human_import_status_name
+.table-holder
+  %table.table.import-jobs
+    %thead
+      %tr
+        %th From FogBugz
+        %th To GitLab
+        %th Status
+    %tbody
+      - @already_added_projects.each do |project|
+        %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+          %td
+            = project.import_source
+          %td
+            %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+          %td.job-status
+            - if project.import_status == 'finished'
+              %span
+                %i.fa.fa-check
+                done
+            - elsif project.import_status == 'started'
+              %i.fa.fa-spinner.fa-spin
+              started
+            - else
+              = project.human_import_status_name
 
-    - @repos.each do |repo|
-      %tr{id: "repo_#{repo.id}"}
-        %td
-          = repo.name
-        %td.import-target
-          = "#{current_user.username}/#{repo.name}"
-        %td.import-actions.job-status
-          = button_tag "Import", class: "btn js-add-to-import"
+      - @repos.each do |repo|
+        %tr{id: "repo_#{repo.id}"}
+          %td
+            = repo.name
+          %td.import-target
+            = "#{current_user.username}/#{repo.name}"
+          %td.import-actions.job-status
+            = button_tag "Import", class: "btn js-add-to-import"
 
-:coffeescript
-  new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}")
+:javascript
+  new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}");
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index ef5524982390fe4817753d043bb934157cd39601..0699321c8c00ac1cf624af837b78d9631cb96ba8 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -9,38 +9,39 @@
 %p
   = button_tag 'Import all projects', class: "btn btn-success js-import-all"
 
-%table.table.import-jobs
-  %thead
-    %tr
-      %th From GitHub
-      %th To GitLab
-      %th Status
-  %tbody
-    - @already_added_projects.each do |project|
-      %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
-        %td
-          = link_to project.import_source, "https://github.com/#{project.import_source}", target: "_blank"
-        %td
-          %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
-        %td.job-status
-          - if project.import_status == 'finished'
-            %span
-              %i.fa.fa-check
-              done
-          - elsif project.import_status == 'started'
-            %i.fa.fa-spinner.fa-spin
-            started
-          - else
-            = project.human_import_status_name
+.table-holder
+  %table.table.import-jobs
+    %thead
+      %tr
+        %th From GitHub
+        %th To GitLab
+        %th Status
+    %tbody
+      - @already_added_projects.each do |project|
+        %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+          %td
+            = link_to project.import_source, "https://github.com/#{project.import_source}", target: "_blank"
+          %td
+            %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+          %td.job-status
+            - if project.import_status == 'finished'
+              %span
+                %i.fa.fa-check
+                done
+            - elsif project.import_status == 'started'
+              %i.fa.fa-spinner.fa-spin
+              started
+            - else
+              = project.human_import_status_name
 
-    - @repos.each do |repo|
-      %tr{id: "repo_#{repo.id}"}
-        %td
-          = link_to repo.full_name, "https://github.com/#{repo.full_name}", target: "_blank"
-        %td.import-target
-          = repo.full_name
-        %td.import-actions.job-status
-          = button_tag "Import", class: "btn js-add-to-import"
+      - @repos.each do |repo|
+        %tr{id: "repo_#{repo.id}"}
+          %td
+            = link_to repo.full_name, "https://github.com/#{repo.full_name}", target: "_blank"
+          %td.import-target
+            = repo.full_name
+          %td.import-actions.job-status
+            = button_tag "Import", class: "btn js-add-to-import"
 
-:coffeescript
-  new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}")
+:javascript
+  new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}");
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index 727f3c7e7fa69b1ace9ff715880c72d012d7f4d4..f4a2b33af21fa9d851f392c9bac0ba364072a85f 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -9,38 +9,39 @@
 %p
   = button_tag 'Import all projects', class: "btn btn-success js-import-all"
 
-%table.table.import-jobs
-  %thead
-    %tr
-      %th From GitLab.com
-      %th To this GitLab instance
-      %th Status
-  %tbody
-    - @already_added_projects.each do |project|
-      %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
-        %td
-          = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
-        %td
-          %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
-        %td.job-status
-          - if project.import_status == 'finished'
-            %span
-              %i.fa.fa-check
-              done
-          - elsif project.import_status == 'started'
-            %i.fa.fa-spinner.fa-spin
-            started
-          - else
-            = project.human_import_status_name
+.table-holder
+  %table.table.import-jobs
+    %thead
+      %tr
+        %th From GitLab.com
+        %th To this GitLab instance
+        %th Status
+    %tbody
+      - @already_added_projects.each do |project|
+        %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+          %td
+            = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
+          %td
+            %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+          %td.job-status
+            - if project.import_status == 'finished'
+              %span
+                %i.fa.fa-check
+                done
+            - elsif project.import_status == 'started'
+              %i.fa.fa-spinner.fa-spin
+              started
+            - else
+              = project.human_import_status_name
 
-    - @repos.each do |repo|
-      %tr{id: "repo_#{repo["id"]}"}
-        %td
-          = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank"
-        %td.import-target
-          = repo["path_with_namespace"]
-        %td.import-actions.job-status
-          = button_tag "Import", class: "btn js-add-to-import"
+      - @repos.each do |repo|
+        %tr{id: "repo_#{repo["id"]}"}
+          %td
+            = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank"
+          %td.import-target
+            = repo["path_with_namespace"]
+          %td.import-actions.job-status
+            = button_tag "Import", class: "btn js-add-to-import"
 
-:coffeescript
-  new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}")
+:javascript
+  new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}");
diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml
index bff7ee7c85d657e532cb754f5a9cb4de1953fd52..71752d21efab67c3722cd75274826d51574d8526 100644
--- a/app/views/import/gitorious/status.html.haml
+++ b/app/views/import/gitorious/status.html.haml
@@ -9,38 +9,39 @@
 %p
   = button_tag 'Import all projects', class: "btn btn-success js-import-all"
 
-%table.table.import-jobs
-  %thead
-    %tr
-      %th From Gitorious.org
-      %th To GitLab
-      %th Status
-  %tbody
-    - @already_added_projects.each do |project|
-      %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
-        %td
-          = link_to project.import_source, "https://gitorious.org/#{project.import_source}", target: "_blank"
-        %td
-          %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
-        %td.job-status
-          - if project.import_status == 'finished'
-            %span
-              %i.fa.fa-check
-              done
-          - elsif project.import_status == 'started'
-            %i.fa.fa-spinner.fa-spin
-            started
-          - else
-            = project.human_import_status_name
+.table-holder
+  %table.table.import-jobs
+    %thead
+      %tr
+        %th From Gitorious.org
+        %th To GitLab
+        %th Status
+    %tbody
+      - @already_added_projects.each do |project|
+        %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+          %td
+            = link_to project.import_source, "https://gitorious.org/#{project.import_source}", target: "_blank"
+          %td
+            %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+          %td.job-status
+            - if project.import_status == 'finished'
+              %span
+                %i.fa.fa-check
+                done
+            - elsif project.import_status == 'started'
+              %i.fa.fa-spinner.fa-spin
+              started
+            - else
+              = project.human_import_status_name
 
-    - @repos.each do |repo|
-      %tr{id: "repo_#{repo.id}"}
-        %td
-          = link_to repo.full_name, "https://gitorious.org/#{repo.full_name}", target: "_blank"
-        %td.import-target
-          = repo.full_name
-        %td.import-actions.job-status
-          = button_tag "Import", class: "btn js-add-to-import"
+      - @repos.each do |repo|
+        %tr{id: "repo_#{repo.id}"}
+          %td
+            = link_to repo.full_name, "https://gitorious.org/#{repo.full_name}", target: "_blank"
+          %td.import-target
+            = repo.full_name
+          %td.import-actions.job-status
+            = button_tag "Import", class: "btn js-add-to-import"
 
-:coffeescript
-  new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}")
+:javascript
+  new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}");
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index e8ec79e72f7fed286f5c4bfa171f48cfa418a02a..8c64fd27e604373ce0d2d5b12dd7f20380c8e7a4 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -17,45 +17,46 @@
   - else
     = button_tag 'Import all projects', class: "btn btn-success js-import-all"
 
-%table.table.import-jobs
-  %thead
-    %tr
-      %th From Google Code
-      %th To GitLab
-      %th Status
-  %tbody
-    - @already_added_projects.each do |project|
-      %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
-        %td
-          = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank"
-        %td
-          %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
-        %td.job-status
-          - if project.import_status == 'finished'
-            %span
-              %i.fa.fa-check
-              done
-          - elsif project.import_status == 'started'
-            %i.fa.fa-spinner.fa-spin
-            started
-          - else
-            = project.human_import_status_name
+.table-holder
+  %table.table.import-jobs
+    %thead
+      %tr
+        %th From Google Code
+        %th To GitLab
+        %th Status
+    %tbody
+      - @already_added_projects.each do |project|
+        %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+          %td
+            = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank"
+          %td
+            %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+          %td.job-status
+            - if project.import_status == 'finished'
+              %span
+                %i.fa.fa-check
+                done
+            - elsif project.import_status == 'started'
+              %i.fa.fa-spinner.fa-spin
+              started
+            - else
+              = project.human_import_status_name
 
-    - @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
-          = "#{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"
+      - @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
+            = "#{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
@@ -66,5 +67,5 @@
     = link_to "import flow", new_import_google_code_path
     again.
 
-:coffeescript
-  new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}")
+:javascript
+  new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}");
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 1a883e20e899dea4f3dd4b7e7b3536b137ef6741..352b8040cf4aa871649584aed4d018a273d8461b 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -18,7 +18,7 @@
       = render partial: 'layouts/collapse_button'
     - if current_user
       = link_to current_user, class: 'sidebar-user' do
-        = image_tag avatar_icon(current_user.email, 60), alt: 'User activity', class: 'avatar avatar s36'
+        = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36'
         .username
           = current_user.username
   .content-wrapper
diff --git a/app/views/layouts/_piwik.html.haml b/app/views/layouts/_piwik.html.haml
index 135e8daca266cca6e27fd2667b73e46be9d36c37..259b4f7cdfc74f5b4e384b6563ce35e55b6fd99f 100644
--- a/app/views/layouts/_piwik.html.haml
+++ b/app/views/layouts/_piwik.html.haml
@@ -1,12 +1,14 @@
+<!-- Piwik -->
 :javascript
   var _paq = _paq || [];
-  _paq.push(["trackPageView"]);
-  _paq.push(["enableLinkTracking"]);
-
+  _paq.push(['trackPageView']);
+  _paq.push(['enableLinkTracking']);
   (function() {
-    var u=(("https:" == document.location.protocol) ? "https" : "http") + "://#{extra_config.piwik_url}/";
-    _paq.push(["setTrackerUrl", u+"piwik.php"]);
-    _paq.push(["setSiteId", "#{extra_config.piwik_site_id}"]);
-    var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript";
-    g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s);
+    var u="//#{extra_config.piwik_url}/";
+    _paq.push(['setTrackerUrl', u+'piwik.php']);
+    _paq.push(['setSiteId', #{extra_config.piwik_site_id}]);
+    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
+    g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
   })();
+<noscript><p><img src="//#{extra_config.piwik_url}/piwik.php?idsite=#{extra_config.piwik_site_id}" style="border:0;" alt="" /></p></noscript>
+<!-- End Piwik Code -->
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index ceb64ce3157f69fd054ece96b2fcfac9f66ea4fc..a44f5762a6b6e65e62e704a53ec4eb43aef5ff2d 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -11,6 +11,8 @@
         = hidden_field_tag :scope, 'merge_requests'
       - elsif current_controller?(:wikis)
         = hidden_field_tag :scope, 'wiki_blobs'
+      - elsif current_controller?(:commits)
+        = hidden_field_tag :scope, 'commits'
       - else
         = hidden_field_tag :search_code, true
 
@@ -23,6 +25,6 @@
 :javascript
   $('.search-input').on('keyup', function(e) {
     if (e.keyCode == 27) {
-      $('.search-input').blur()
+      $('.search-input').blur();
     }
-  })
+  });
diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml
index af2545a22d86ecb1c37be5edbca3e44c5333092d..dcda04a4638f49e46382e5f0445afdcdfa05bed8 100644
--- a/app/views/layouts/ci/_nav_admin.html.haml
+++ b/app/views/layouts/ci/_nav_admin.html.haml
@@ -9,23 +9,25 @@
   = nav_link path: 'projects#index' do
     = link_to ci_admin_projects_path do
       = icon('list-alt fw')
-      Projects
+      %span
+        Projects
   = nav_link path: 'events#index' do
     = link_to ci_admin_events_path do
       = icon('book fw')
-      Events
+      %span
+        Events
   = nav_link path: ['runners#index', 'runners#show'] do
     = link_to ci_admin_runners_path do
       = icon('cog fw')
-      Runners
-      %small.pull-right
-        = Ci::Runner.count(:all)
+      %span
+        Runners
+        %span.count= Ci::Runner.count(:all)
   = nav_link path: 'builds#index' do
     = link_to ci_admin_builds_path do
       = icon('link fw')
-      Builds
-      %small.pull-right
-        = Ci::Build.count(:all)
+      %span
+        Builds
+        %span.count= Ci::Build.count(:all)
   = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
     = link_to ci_admin_application_settings_path do
       = icon('cogs fw')
diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml
index bb5ec727bffbcf55f08f1802b9fd2aa35b26ef3f..ab3e29c3f424c63f106f62c28ddf0fb88cb75a3d 100644
--- a/app/views/layouts/ci/_page.html.haml
+++ b/app/views/layouts/ci/_page.html.haml
@@ -15,7 +15,7 @@
       = render partial: 'layouts/collapse_button'
     - if current_user
       = link_to current_user, class: 'sidebar-user' do
-        = image_tag avatar_icon(current_user.email, 60), alt: 'User activity', class: 'avatar avatar s36'
+        = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36'
         .username
           = current_user.username
   .content-wrapper
diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml
deleted file mode 100644
index 15478c3f5bc5f18411f4e58310918f11db5de923..0000000000000000000000000000000000000000
--- a/app/views/layouts/ci/project.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-!!! 5
-%html{ lang: "en"}
-  = render 'layouts/head'
-  %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page}
-    - header_title @project.name, ci_project_path(@project)
-    - if current_user
-      = render "layouts/header/default", title: header_title
-    - else
-      = render "layouts/header/public", title: header_title
-
-    = render 'layouts/ci/page', sidebar: 'nav_project'
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index c31b1cbe9a8ab8aaa030154f203a182d4365b46c..c08a7b8074474222551ef746e14cda1269c08bf8 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -21,6 +21,11 @@
             %li
               = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do
                 = icon('plus fw')
+          - if Gitlab::Sherlock.enabled?
+            %li
+              = link_to sherlock_transactions_path, title: 'Sherlock Transactions',
+                data: {toggle: 'tooltip', placement: 'bottom'} do
+                = icon('tachometer fw')
           %li
             = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do
               = icon('sign-out')
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index eb35af22b93836d1af2fb7052c99f2875ac4f39e..319352876b4ec648b8b1315ad1669790cc979430 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,9 +1,9 @@
 %ul.nav.nav-sidebar
   = nav_link do
-    = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do
+    = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
       = icon('caret-square-o-left fw')
       %span
-        Back to dashboard
+        Go to dashboard
 
   %li.separate-item
 
diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml
index 8075fe32fbce4b261630dafad8cc3b3b1d0cf372..c8411521f3692f40746882e3da47e8a8611e26e0 100644
--- a/app/views/layouts/nav/_group_settings.html.haml
+++ b/app/views/layouts/nav/_group_settings.html.haml
@@ -1,9 +1,9 @@
 %ul.nav.nav-sidebar
   = nav_link do
-    = link_to group_path(@group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do
+    = link_to group_path(@group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do
       = icon('caret-square-o-left fw')
       %span
-        Back to group
+        Go to group
 
   %li.separate-item
 
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 5a47b8e6db2ec0656e9526a5e474f09ef6246bce..0f3a793e30be18ac993bdc59ee800b60144ae48d 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,9 +1,9 @@
 %ul.nav.nav-sidebar
   = nav_link do
-    = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do
+    = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
       = icon('caret-square-o-left fw')
       %span
-        Back to dashboard
+        Go to dashboard
 
   %li.separate-item
 
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index e4c285d802380d51b46445843c91a8843ed58e91..2b91d7721f932446cd27516eaf810409d8b67657 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,16 +1,16 @@
 %ul.nav.nav-sidebar
   - if @project.group
     = nav_link do
-      = link_to group_path(@project.group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do
+      = link_to group_path(@project.group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do
         = icon('caret-square-o-left fw')
         %span
-          Back to group
+          Go to group
   - else
     = nav_link do
-      = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do
+      = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
         = icon('caret-square-o-left fw')
         %span
-          Back to dashboard
+          Go to dashboard
 
   %li.separate-item
 
@@ -32,12 +32,20 @@
           Files
 
   - if project_nav_tab? :commits
-    = nav_link(controller: %w(commit commits compare repositories tags branches builds)) do
+    = nav_link(controller: %w(commit commits compare repositories tags branches releases)) do
       = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
         = icon('history fw')
         %span
           Commits
 
+  - if project_nav_tab? :builds
+    = nav_link(controller: %w(builds)) do
+      = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds', data: {placement: 'right'} do
+        = icon('cubes fw')
+        %span
+          Builds
+          %span.count.builds_counter= @project.ci_builds.running_or_pending.count(:all)
+
   - if project_nav_tab? :network
     = nav_link(controller: %w(network)) do
       = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index 954dbe5d2b9079c39f6fd76c1ee8bf8545e9c6b0..a59939ccd31f5335652e8c161fdbca11057598af 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -1,9 +1,9 @@
 %ul.nav.nav-sidebar
   = nav_link do
-    = link_to project_path(@project), title: 'Back to project', data: {placement: 'right'}, class: 'back-link' do
+    = link_to project_path(@project), title: 'Go to project', data: {placement: 'right'}, class: 'back-link' do
       = icon('caret-square-o-left fw')
       %span
-        Back to project
+        Go to project
 
   %li.separate-item
 
@@ -65,8 +65,3 @@
           = icon('share fw')
           %span
             CI Services
-      = nav_link path: 'events#index' do
-        = link_to ci_project_events_path(@project.gitlab_ci_project) do
-          = icon('book fw')
-          %span
-            CI Events
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 2f7d7e86f563c330e7bd9b0d39b74baa2f41a22e..f58b9bd6ba60b263b7221fe78d2689d4f0563df7 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -40,5 +40,9 @@
             Reply to this email directly or
             #{link_to "view it on GitLab", @target_url}.
           - else
-            #{link_to "View it on GitLab", @target_url}
+            #{link_to "View it on GitLab", @target_url}.
+          %br
+          You're receiving this email because of your account on #{Gitlab.config.gitlab.host}.
+          If you'd like to receive fewer emails, you can adjust your notification settings.
+
           = email_action @target_url
diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml
index 3fd4b04ac8456a42f591a3f77fa197866e873c72..00cb4aa24cc28b60b4e1c2c98efc7eff2050a1b6 100644
--- a/app/views/notify/_note_message.html.haml
+++ b/app/views/notify/_note_message.html.haml
@@ -1,2 +1,2 @@
 %div
-  = markdown(@note.note, reference_only_path: false)
+  = markdown(@note.note, pipeline: :email)
diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml
index 53a068be52e5b1c53a380f32a9bcf91cc480638e..d3b799fca23840696c50e71d536b4545f73aa13b 100644
--- a/app/views/notify/new_issue_email.html.haml
+++ b/app/views/notify/new_issue_email.html.haml
@@ -1,5 +1,5 @@
 -if @issue.description
-  = markdown(@issue.description, reference_only_path: false)
+  = markdown(@issue.description, pipeline: :email)
 
 - if @issue.assignee_id.present?
   %p
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index 5b7dd117c16afd159c0a427bc7022dc2a83e04e3..90ebdfc3fe278db5e06c928f12fb62e04787ce6e 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -6,4 +6,4 @@
     Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
 
 -if @merge_request.description
-  = markdown(@merge_request.description, reference_only_path: false)
+  = markdown(@merge_request.description, pipeline: :email)
diff --git a/app/views/profiles/_event_table.html.haml b/app/views/profiles/_event_table.html.haml
index c19ac429d52da7943f6197cce52f4c8e8b38002e..58af79716a75d4b1e014930a0a4165eeb48c58c1 100644
--- a/app/views/profiles/_event_table.html.haml
+++ b/app/views/profiles/_event_table.html.haml
@@ -1,16 +1,17 @@
-%table.table#audits
-  %thead
-    %tr
-      %th Action
-      %th When
-
-  %tbody
-    - events.each do |event|
+.table-holder
+  %table.table#audits
+    %thead
       %tr
-        %td
-          %span
-            Signed in with 
-            %b= event.details[:with]
-            authentication
-        %td #{time_ago_in_words event.created_at} ago
+        %th Action
+        %th When
+
+    %tbody
+      - events.each do |event|
+        %tr
+          %td
+            %span
+              Signed in with 
+              %b= event.details[:with]
+              authentication
+          %td #{time_ago_in_words event.created_at} ago
 = paginate events, theme: "gitlab"
diff --git a/app/views/profiles/notifications/_settings.html.haml b/app/views/profiles/notifications/_settings.html.haml
index 2c85d2a9b2bb6223f033fafd3a24f99105c68f75..742c5c4b68de4567039022a64e500af179628ed3 100644
--- a/app/views/profiles/notifications/_settings.html.haml
+++ b/app/views/profiles/notifications/_settings.html.haml
@@ -14,4 +14,4 @@
     = form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do
       = hidden_field_tag :notification_type, type, id: dom_id(membership, 'notification_type')
       = hidden_field_tag :notification_id, membership.id, id: dom_id(membership, 'notification_id')
-      = select_tag :notification_level, options_for_select(Notification.options_with_labels, notification.level), class: 'trigger-submit'
+      = select_tag :notification_level, options_for_select(Notification.options_with_labels, notification.level), class: 'form-control trigger-submit'
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 01e285a8dfa4cdf55bad3ea105a79a85da1ee73b..cc41d7dd8130ab787d023d41bf01d32e1529bc9c 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -38,7 +38,7 @@
         .col-sm-10
           = f.select :layout, layout_choices, {}, class: 'form-control'
           .help-block
-            Choose between fixed (max. 1200px) and fluid (100%) application layout
+            Choose between fixed (max. 1200px) and fluid (100%) application layout.
       .form-group
         = f.label :dashboard, class: 'control-label' do
           Default Dashboard
@@ -52,6 +52,6 @@
         .col-sm-10
           = f.select :project_view, project_view_choices, {}, class: 'form-control'
           .help-block
-            Choose what content you want to see when visit project page
+            Choose what content you want to see on a project's home page.
     .panel-footer
       = f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 47412e2ef0c2cd9652389da2fc4bdc7deb900537..ac7355dde1fc1f6a112638ff5ed62dc2d4148e5f 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -68,7 +68,7 @@
 
     .col-md-5
       .light-well
-        = image_tag avatar_icon(@user.email, 160), alt: '', class: 'avatar s160'
+        = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160'
 
         .clearfix
           .profile-avatar-form-option
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index c2683bc62194754b8818c46d9839c7da6ff71b01..101880bd1053451877c8d31521562dd63a5a6ffa 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -1,4 +1,3 @@
-= render 'projects/last_push'
 .gray-content-block.activity-filter-block
   - if current_user
     .pull-right
@@ -9,5 +8,5 @@
 .content_list{:"data-href" => activity_project_path(@project)}
 = spinner
 
-:coffeescript
-  new Activities()
+:javascript
+  new Activities();
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..fa978325ddd5923f65020d669bb1418041fa3d86
--- /dev/null
+++ b/app/views/projects/_files.html.haml
@@ -0,0 +1,6 @@
+#tree-holder.tree-holder.clearfix
+  .gray-content-block.second-block
+    = render 'projects/tree/tree_header', tree: @tree
+
+  = render 'projects/tree/tree_content', tree: @tree
+
diff --git a/app/views/projects/_last_commit.html.haml b/app/views/projects/_last_commit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..7e1ee2b7fc1ab92aa0c46a0f60d45528a016e883
--- /dev/null
+++ b/app/views/projects/_last_commit.html.haml
@@ -0,0 +1,12 @@
+.project-last-commit
+  - ci_commit = project.ci_commit(commit.sha)
+  - if ci_commit
+    = link_to ci_status_path(ci_commit), class: "ci-status ci-#{ci_commit.status}" do
+      = ci_status_icon(ci_commit)
+      = ci_commit.status
+
+  = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
+  = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message"
+  &middot;
+  #{time_ago_with_tooltip(commit.committed_date, skip_js: true)} by
+  = commit_author_link(commit, avatar: true, size: 24)
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 507757f6a2bb5b1121047ac9fe827d4e52089811..7b21095ea3e8637688f32cbf4ef94bf2e6f4379b 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -2,10 +2,10 @@
   .md-header.clearfix
     %ul.center-top-menu
       %li.active
-        = link_to '#md-write-holder', class: 'js-md-write-button', tabindex: '-1' do
+        %a.js-md-write-button(href="#md-write-holder" tabindex="-1")
           Write
       %li
-        = link_to '#md-preview-holder', class: 'js-md-preview-button', tabindex: '-1' do
+        %a.js-md-preview-button(href="md-preview-holder" tabindex="-1")
           Preview
 
     - if defined?(referenced_users) && referenced_users
diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml
index 5bc1999ec9d7e0a8dba7990210bac76e46e150ef..b5ef0aca54035a77f2e684506350124d77561b99 100644
--- a/app/views/projects/_readme.html.haml
+++ b/app/views/projects/_readme.html.haml
@@ -1,12 +1,9 @@
 - if readme = @repository.readme
-  %article.readme-holder#README
-    .clearfix
-      .pull-right
-        &nbsp;
-        - if can?(current_user, :push_code, @project)
-          = link_to namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light' do
-            %i.fa-align.fa.fa-pencil
-    .wiki
+  %article.readme-holder
+    .pull-right
+      - if can?(current_user, :push_code, @project)
+        = link_to icon('pencil'), namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light edit-project-readme'
+    .file-content.wiki
       = cache(readme_cache_key) do
         = render_readme(readme)
 - else
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index 6a41cdbc907a433e216494e1910a4a7c9e897201..63ebfc9381f25bf82a40e5a9fe24904fe8d2cc99 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -1,10 +1,10 @@
 .zennable
-  %input#zen-toggle-comment.zen-toggle-comment{ tabindex: '-1', type: 'checkbox' }
+  %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox")
   .zen-backdrop
     - classes << ' js-gfm-input markdown-area'
     = f.text_area attr, class: classes, placeholder: ''
-    = link_to nil, class: 'zen-enter-link', tabindex: '-1' do
+    %a.zen-enter-link(tabindex="-1" href="#")
       %i.fa.fa-expand
       Edit in fullscreen
-    = link_to nil, class: 'zen-leave-link' do
+    %a.zen-leave-link(href="#")
       %i.fa.fa-compress
diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml
index 555ed76426dd07de53c2222a3486949f4d1e061e..69fa4ad37c4bba65f5d3c93d38e9a1fce62a2e2f 100644
--- a/app/views/projects/activity.html.haml
+++ b/app/views/projects/activity.html.haml
@@ -1,4 +1,6 @@
 - page_title "Activity"
 - header_title project_title(@project, "Activity", activity_project_path(@project))
 
+= render 'projects/last_push'
+
 = render 'projects/activity'
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index a1ae1397584ea63494c8f788c02d32181c88114a..42f632b38efea9748a4cf361a96750bab1a46fab 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -1,19 +1,22 @@
-%ul.breadcrumb.repo-breadcrumb
-  %li
-    %i.fa.fa-angle-right
-    = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
-      = @project.path
-  - tree_breadcrumbs(@tree, 6) do |title, path|
+.gray-content-block.top-block
+  .tree-ref-holder
+    = render 'shared/ref_switcher', destination: 'blob', path: @path
+
+  %ul.breadcrumb.repo-breadcrumb
     %li
-      - if path
-        - if path.end_with?(@path)
-          = link_to namespace_project_blob_path(@project.namespace, @project, path) do
-            %strong
-              = truncate(title, length: 40)
+      = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
+        = @project.path
+    - tree_breadcrumbs(@tree, 6) do |title, path|
+      %li
+        - if path
+          - if path.end_with?(@path)
+            = link_to namespace_project_blob_path(@project.namespace, @project, path) do
+              %strong
+                = truncate(title, length: 40)
+          - else
+            = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
         - else
-          = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
-      - else
-        = link_to title, '#'
+          = link_to title, '#'
 
 %ul.blob-commit-info.hidden-xs
   - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
index cb1567a2e68b527a5235f30328f5ceea65e13496..a0fc8bbd75201e077631c45ed2ce5285b798b1a8 100644
--- a/app/views/projects/blob/_new_dir.html.haml
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -21,5 +21,5 @@
               = submit_tag "Create directory", class: 'btn btn-primary btn-create'
               = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
 
-:coffeescript
+:javascript
   disableButtonIfAnyEmptyField($("#dir-create-form"), ".form-control", ".btn-create");
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index e27f17075273670bae618d1498232fff394678ad..a1c54e731f04c2553049ca2f78740a77165cc1e9 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -26,6 +26,6 @@
               = button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all'
               = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
 
-:coffeescript
-  disableButtonIfEmptyField $('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file'
-  new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}')
+:javascript
+  disableButtonIfEmptyField($('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file');
+  new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}');
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index fa4be4a1bc43ed30d772306f074c44ad7c1b48a4..f52b89f69210f1aff6ded38209e88dd99f782ae5 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -3,9 +3,6 @@
 
 = render 'projects/last_push'
 
-%div.tree-ref-holder
-  = render 'shared/ref_switcher', destination: 'blob', path: @path
-
 %div#tree-holder.tree-holder
   = render 'blob', blob: @blob
 
diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml
index 68326e65d85093000d6408c4bcbb72a68a56e4ae..22d77dda938d9713c2dfd6b50fc0a18ba4f602a1 100644
--- a/app/views/projects/branches/_commit.html.haml
+++ b/app/views/projects/branches/_commit.html.haml
@@ -1,5 +1,5 @@
-.branch-commit.light
-  = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
+.branch-commit
+  = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id"
   &middot;
   %span.str-truncated
     = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
diff --git a/app/views/projects/builds/_header_title.html.haml b/app/views/projects/builds/_header_title.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..082dab1f5b06bc89483dd370ca709f1e1d9236e5
--- /dev/null
+++ b/app/views/projects/builds/_header_title.html.haml
@@ -0,0 +1 @@
+- header_title project_title(@project, "Builds", project_builds_path(@project))
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..dab7164153ff119783297a10e48d2f53794aff2e
--- /dev/null
+++ b/app/views/projects/builds/index.html.haml
@@ -0,0 +1,53 @@
+- page_title "Builds"
+= render "header_title"
+
+.project-issuable-filter
+  .controls
+    - if @ci_project && current_user && can?(current_user, :manage_builds, @project)
+      .pull-left.hidden-xs
+        - if @all_builds.running_or_pending.any?
+          = link_to 'Cancel all', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
+
+  %ul.center-top-menu
+    %li{class: ('active' if @scope.nil?)}
+      = link_to project_builds_path(@project) do
+        Running
+        %span.badge.js-running-count= @all_builds.running_or_pending.count(:id)
+
+    %li{class: ('active' if @scope == 'finished')}
+      = link_to project_builds_path(@project, scope: :finished) do
+        Finished
+        %span.badge.js-running-count= @all_builds.finished.count(:id)
+
+    %li{class: ('active' if @scope == 'all')}
+      = link_to project_builds_path(@project, scope: :all) do
+        All
+        %span.badge.js-totalbuilds-count= @all_builds.count(:id)
+
+.gray-content-block
+  #{(@scope || 'running').capitalize} builds from this project
+
+%ul.content-list
+  - if @builds.blank?
+    %li
+      .nothing-here-block No builds to show
+  - else
+    .table-holder
+      %table.table.builds
+        %thead
+          %tr
+            %th Status
+            %th Build ID
+            %th Commit
+            %th Ref
+            %th Stage
+            %th Name
+            %th Duration
+            %th Finished at
+            %th
+
+        - @builds.each do |build|
+          = render 'projects/commit_statuses/commit_status', commit_status: build, commit_sha: true, stage: true, allow_retry: true
+
+    = paginate @builds, theme: 'gitlab'
+
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 9c3ae622b728bd28f51fc63c991f67b1b47b4b39..3374d5432a5276f868c45a17cd1440b420efbd2d 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -1,10 +1,13 @@
+- page_title "#{@build.name} (#{@build.id})", "Builds"
+= render "header_title"
+
 .build-page
   .gray-content-block
-    Build for commit
+    Build ##{@build.id} for commit
     %strong.monospace
       = link_to @build.commit.short_sha, ci_status_path(@build.commit)
     from
-    %code #{@build.ref}
+    = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
 
   #up-build-trace
   - if @commit.matrix_for_ref?(@build.ref)
@@ -20,12 +23,12 @@
                 = build.id
 
 
-      - unless @commit.latest_builds_for_ref(@build.ref).include?(@build)
+      - if @build.retried?
         %li.active
           %a
             Build ##{@build.id}
             &middot;
-            %i.fa.fa-warning-sign
+            %i.fa.fa-warning
             This build was retried.
 
   .gray-content-block.second-block
@@ -37,7 +40,26 @@
             %i.fa.fa-time
             #{duration_in_words(@build.finished_at, @build.started_at)}
         .pull-right
-          = @build.updated_at.stamp('19:00 Aug 27')
+          #{time_ago_with_tooltip(@build.finished_at) if @build.finished_at}
+
+  - if @build.show_warning?
+    - unless @build.any_runners_online?
+      .bs-callout.bs-callout-warning
+        %p
+          - if no_runners_for_project?(@build.project)
+            This build is stuck, because the project doesn't have any runners online assigned to it.
+          - elsif @build.tags.any?
+            This build is stuck, because you don't have any active runners online with any of these tags assigned to them:
+            - @build.tags.each do |tag|
+              %span.label.label-primary
+                = tag
+          - else
+            This build is stuck, because you don't have any active runners that can run this build.
+
+          %br
+          Go to
+          = link_to namespace_project_runners_path(@build.gl_project.namespace, @build.gl_project) do
+            Runners page
 
   .row.prepend-top-default
     .col-md-9
@@ -68,13 +90,13 @@
 
       .build-widget
         %h4.title
-          Build
+          Build ##{@build.id}
           - if current_user && can?(current_user, :manage_builds, @project)
             .pull-right
-              - if @build.active?
-                = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-danger'
-              - elsif @build.commands.present?
-                = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary', method: :post
+              - if @build.cancel_url
+                = link_to "Cancel", @build.cancel_url, class: 'btn btn-sm btn-danger', method: :post
+              - elsif @build.retry_url
+                = link_to "Retry", @build.retry_url, class: 'btn btn-sm btn-primary', method: :post
 
         - if @build.duration
           %p
@@ -82,15 +104,15 @@
             #{duration_in_words(@build.finished_at, @build.started_at)}
         %p
           %span.attr-name Created:
-          #{time_ago_in_words(@build.created_at)} ago
+          #{time_ago_with_tooltip(@build.created_at)}
         - if @build.finished_at
           %p
             %span.attr-name Finished:
-            #{time_ago_in_words(@build.finished_at)} ago
+            #{time_ago_with_tooltip(@build.finished_at)}
         %p
           %span.attr-name Runner:
           - if @build.runner && current_user && current_user.admin
-            \#{link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id)}
+            = link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id)
           - elsif @build.runner
             \##{@build.runner.id}
 
@@ -115,10 +137,11 @@
         %h4.title
           Commit
           .pull-right
-            %small #{build_commit_link @build}
+            %small 
+              = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace"
         %p
           %span.attr-name Branch:
-          #{build_ref_link @build}
+          = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
         %p
           %span.attr-name Author:
           #{@build.commit.git_author_name}
@@ -136,7 +159,9 @@
 
       - if @builds.present?
         .build-widget
-          %h4.title #{pluralize(@builds.count, "other build")} for #{@build.short_sha}:
+          %h4.title #{pluralize(@builds.count(:id), "other build")} for 
+          = succeed ":" do
+            = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace"
           %table.table.builds
             - @builds.each_with_index do |build, i|
               %tr.build
@@ -152,8 +177,5 @@
                 %td.status= build.status
 
 
-          = paginate @builds
-
-
   :javascript
-    new CiBuild("#{namespace_project_build_path(@project.namespace, @project, @build)}", "#{@build.status}")
+    new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}")
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index 4580c912692e79e4a0d34f762e3eb80a95785086..bed2b16249e62aa888a33739f9f6344766450eda 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -20,6 +20,10 @@
             New snippet
       - if can?(current_user, :push_code, @project)
         %li.divider
+        %li
+          = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), title: 'New file' do
+            = icon('file fw')
+            New file
         %li
           = link_to new_namespace_project_branch_path(@project.namespace, @project) do
             = icon('code-fork fw')
diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml
index 3bc2daeec4ed10dee40e8255083bb4c0669e4b78..3e83ec3912f6728996d70611e8d0da2b6cdc8f02 100644
--- a/app/views/projects/buttons/_notifications.html.haml
+++ b/app/views/projects/buttons/_notifications.html.haml
@@ -1,14 +1,20 @@
-- return unless @membership
+- case @membership
+- when ProjectMember
+  = form_tag profile_notifications_path, method: :put, remote: true, class: 'inline', id: 'notification-form' do
+    = hidden_field_tag :notification_type, 'project'
+    = hidden_field_tag :notification_id, @membership.id
+    = hidden_field_tag :notification_level
+    %span.dropdown
+      %a.dropdown-new.btn.notifications-btn#notifications-button{href: '#', "data-toggle" => "dropdown"}
+        = icon('bell')
+        = notification_label(@membership)
+        = icon('angle-down')
+      %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
+        - Notification.project_notification_levels.each do |level|
+          = notification_list_item(level, @membership)
 
-= form_tag profile_notifications_path, method: :put, remote: true, class: 'inline', id: 'notification-form' do
-  = hidden_field_tag :notification_type, 'project'
-  = hidden_field_tag :notification_id, @membership.id
-  = hidden_field_tag :notification_level
-  %span.dropdown
-    %a.dropdown-new.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"}
-      = icon('bell')
-      = notification_label(@membership)
-      = icon('angle-down')
-    %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
-      - Notification.project_notification_levels.each do |level|
-        = notification_list_item(level, @membership)
+- when GroupMember
+  .btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
+    = icon('bell')
+    = notification_label(@membership)
+    = icon('angle-down')
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 3501dddefbe3a6c6f714ffa02d933ec0ce909f1d..06583902035de013d505250661d194a988b7ffe1 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -4,11 +4,13 @@
     %span.count
       = @project.star_count
 
-  :coffeescript
-    $('.project-home-panel .toggle-star').on 'ajax:success', (e, data, status, xhr) ->
-      $(@).replaceWith(data.html)
-    .on 'ajax:error', (e, xhr, status, error) ->
-      new Flash('Star toggle failed. Try again later.', 'alert')
+  :javascript
+    $('.project-home-panel .toggle-star').on('ajax:success', function (e, data, status, xhr) {
+      $(this).replaceWith(data.html);
+    })
+    .on('ajax:error', function (e, xhr, status, error) {
+      new Flash('Star toggle failed. Try again later.', 'alert');
+    });
 
 - else
   = link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do
diff --git a/app/views/projects/ci_services/edit.html.haml b/app/views/projects/ci_services/edit.html.haml
index bcc5832792fea10b70d29a9fb15633f06e001dd0..dacb6b4f6f4d159ade72c501d4bf0332a8019333 100644
--- a/app/views/projects/ci_services/edit.html.haml
+++ b/app/views/projects/ci_services/edit.html.haml
@@ -1 +1,2 @@
+- page_title @service.title, "CI Services"
 = render 'form'
diff --git a/app/views/projects/ci_services/index.html.haml b/app/views/projects/ci_services/index.html.haml
index c78b21884a314c9aa779ca7f7dc788d27ea70c9d..3f26c7851d84d5f6d3b959bd630cfb573d1cb75f 100644
--- a/app/views/projects/ci_services/index.html.haml
+++ b/app/views/projects/ci_services/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "CI Services"
 %h3.page-title Project services
 %p.light Project services allow you to integrate GitLab CI with other applications
 
@@ -6,7 +7,7 @@
     %tr
       %th
       %th Service
-      %th Desription
+      %th Description
       %th Last edit
   - @services.sort_by(&:title).each do |service|
     %tr
diff --git a/app/views/projects/ci_settings/_form.html.haml b/app/views/projects/ci_settings/_form.html.haml
index d711413c6b956b147c9ddc3a7226e7a84fa6c8e8..ee6b8885e2def0781d600e8f3136749cc15bb4cc 100644
--- a/app/views/projects/ci_settings/_form.html.haml
+++ b/app/views/projects/ci_settings/_form.html.haml
@@ -102,9 +102,10 @@
               %code \(\d+.\d+\%\) covered
             %li
               pytest-cov (Python) -
-              %code \d+\%$
-
-
+              %code \d+\%\s*$
+            %li
+              phpunit --coverage-text --colors=never (PHP) -
+              %code ^\s*Lines:\s*\d+.\d+\%
 
   %fieldset
     %legend Advanced settings
diff --git a/app/views/projects/ci_settings/_no_runners.html.haml b/app/views/projects/ci_settings/_no_runners.html.haml
index 33038c529780ed45aeba6ccbd2a4a2576aa33faf..1374e6680f9506e93eccc673ec976d797ea3c024 100644
--- a/app/views/projects/ci_settings/_no_runners.html.haml
+++ b/app/views/projects/ci_settings/_no_runners.html.haml
@@ -5,4 +5,4 @@
     You can add Specific runner for this project on Runners page
 
     - if current_user.admin
-      or add Shared runner for whole application in admin are.
+      or add Shared runner for whole application in admin area.
diff --git a/app/views/projects/ci_settings/edit.html.haml b/app/views/projects/ci_settings/edit.html.haml
index eedf484bf00f2105548175ba9d749e878a76e8d8..665556f5c204c6404202f102c0c6f8332423d4f8 100644
--- a/app/views/projects/ci_settings/edit.html.haml
+++ b/app/views/projects/ci_settings/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "CI Settings"
 - if @ci_project.generated_yaml_config
   %p.alert.alert-danger
     CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_ci_project_path(@ci_project)}
diff --git a/app/views/projects/ci_web_hooks/index.html.haml b/app/views/projects/ci_web_hooks/index.html.haml
index 6aebd7cfc4dd0cc35b6eac8cc7060b996c284b0e..2998fb08ff1a5678cd99fcd0ece1f3e5427857de 100644
--- a/app/views/projects/ci_web_hooks/index.html.haml
+++ b/app/views/projects/ci_web_hooks/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "CI Web Hooks"
 %h3.page-title
   CI Web hooks
 
@@ -20,17 +21,18 @@
 
 -if @web_hooks.any?
   %h4 Activated web hooks (#{@web_hooks.count})
-  %table.table
-    - @web_hooks.each do |hook|
-      %tr
-        %td
-          .clearfix
-            %span.monospace= hook.url
-        %td
-          .pull-right
-            - if @ci_project.commits.any?
-              = link_to 'Test Hook', test_namespace_project_ci_web_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped"
-            = link_to 'Remove', namespace_project_ci_web_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
+  .table-holder
+    %table.table
+      - @web_hooks.each do |hook|
+        %tr
+          %td
+            .clearfix
+              %span.monospace= hook.url
+          %td
+            .pull-right
+              - if @ci_project.commits.any?
+                = link_to 'Test Hook', test_namespace_project_ci_web_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped"
+              = link_to 'Remove', namespace_project_ci_web_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
 
 %h4 Web Hook data example
 
diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml
index a634ae5dfda5e623178a5d34399e55b7465ce608..c73ba74f5efc3a1c15ce09492cd7a40c42d86385 100644
--- a/app/views/projects/commit/_ci_menu.html.haml
+++ b/app/views/projects/commit/_ci_menu.html.haml
@@ -2,6 +2,8 @@
   = nav_link(path: 'commit#show') do
     = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do
       Changes
-  = nav_link(path: 'commit#ci') do
-    = link_to ci_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
+      %span.badge= @diffs.count
+  = nav_link(path: 'commit#builds') do
+    = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
       Builds
+      %span.badge= @builds.count(:id)
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index fbf0a9ec0c306482d3c12046f6ab162d2d44cd88..776768537d0ac9efb4efa6ff93fc2c9e7c890209 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -19,7 +19,7 @@
 
 %p
   %span.light Commit
-  = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit)
+  = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
 .commit-info-row
   %span.light Authored by
   %strong
@@ -36,7 +36,7 @@
 .commit-info-row
   %span.cgray= pluralize(@commit.parents.count, "parent")
   - @commit.parents.each do |parent|
-    = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent)
+    = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace"
 
 - if @ci_commit
   .pull-right
@@ -55,5 +55,5 @@
     %pre.commit-description
       = preserve(gfm(escape_once(@commit.description)))
 
-:coffeescript
-  $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}")
+:javascript
+  $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}");
diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..00cf9c761025df9d9aee80c697f969e27510a6ca
--- /dev/null
+++ b/app/views/projects/commit/builds.html.haml
@@ -0,0 +1,72 @@
+- page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits"
+= render "projects/commits/header_title"
+= render "commit_box"
+= render "ci_menu"
+
+
+- if @ci_commit.yaml_errors.present?
+  .bs-callout.bs-callout-danger
+    %h4 Found errors in your .gitlab-ci.yml:
+    %ul
+      - @ci_commit.yaml_errors.split(",").each do |error|
+        %li= error
+
+- unless @ci_commit.ci_yaml_file
+  .bs-callout.bs-callout-warning
+    \.gitlab-ci.yml not found in this commit
+
+.gray-content-block.second-block
+  Latest builds
+
+  .pull-right
+    - if @ci_commit.duration > 0
+      %i.fa.fa-time
+      #{time_interval_in_words @ci_commit.duration}
+
+    &nbsp;
+
+    - if @ci_project && current_user && can?(current_user, :manage_builds, @project)
+      - if @ci_commit.builds.latest.failed.any?(&:retryable?)
+        = link_to "Retry failed", retry_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-primary', method: :post
+
+      - if @ci_commit.builds.running_or_pending.any?
+        = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-danger', method: :post
+
+.table-holder
+  %table.table.builds
+    %thead
+      %tr
+        %th Status
+        %th Build ID
+        %th Ref
+        %th Stage
+        %th Name
+        %th Duration
+        %th Finished at
+        - if @ci_project && @ci_project.coverage_enabled?
+          %th Coverage
+        %th
+    - @ci_commit.refs.each do |ref|
+      = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered,
+               locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true }
+
+- if @ci_commit.retried.any?
+  .gray-content-block.second-block
+    Retried builds
+
+  .table-holder
+    %table.table.builds
+      %thead
+        %tr
+          %th Status
+          %th Build ID
+          %th Ref
+          %th Stage
+          %th Name
+          %th Duration
+          %th Finished at
+          - if @ci_project && @ci_project.coverage_enabled?
+            %th Coverage
+          %th
+      = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried,
+               locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true }
diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml
deleted file mode 100644
index ca71a91af156d0ececae7c1e06001b7b79953ef7..0000000000000000000000000000000000000000
--- a/app/views/projects/commit/ci.html.haml
+++ /dev/null
@@ -1,67 +0,0 @@
-- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
-= render "projects/commits/header_title"
-= render "commit_box"
-= render "ci_menu"
-
-
-- if @ci_commit.yaml_errors.present?
-  .bs-callout.bs-callout-danger
-    %h4 Found errors in your .gitlab-ci.yml:
-    %ul
-      - @ci_commit.yaml_errors.split(",").each do |error|
-        %li= error
-
-- unless @ci_commit.ci_yaml_file
-  .bs-callout.bs-callout-warning
-    \.gitlab-ci.yml not found in this commit
-
-.gray-content-block.second-block
-  Latest builds
-
-  .pull-right
-    - if @ci_commit.duration > 0
-      %i.fa.fa-time
-      #{time_interval_in_words @ci_commit.duration}
-
-    &nbsp;
-
-    - if @ci_project && current_user && can?(current_user, :manage_builds, @project)
-      - if @ci_commit.builds.running_or_pending.any?
-        = link_to "Cancel all", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-danger'
-
-%table.table.builds
-  %thead
-    %tr
-      %th Status
-      %th Build ID
-      %th Ref
-      %th Stage
-      %th Name
-      %th Duration
-      %th Finished at
-      - if @ci_project && @ci_project.coverage_enabled?
-        %th Coverage
-      %th
-  - @ci_commit.refs.each do |ref|
-    = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered,
-             locals: { coverage: @ci_project.try(:coverage_enabled?), allow_retry: true }
-
-- if @ci_commit.retried.any?
-  .gray-content-block.second-block
-    Retried builds
-
-  %table.table.builds
-    %thead
-      %tr
-        %th Status
-        %th Build ID
-        %th Ref
-        %th Stage
-        %th Name
-        %th Duration
-        %th Finished at
-        - if @ci_project && @ci_project.coverage_enabled?
-          %th Coverage
-        %th
-    = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried,
-             locals: { coverage: @ci_project.try(:coverage_enabled?) }
diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml
index 7314f8e79d3ad3771f7778d0ceb078430ef06ce8..c255559b88ca32fd570cb279c7870a8f4e0015ae 100644
--- a/app/views/projects/commit_statuses/_commit_status.html.haml
+++ b/app/views/projects/commit_statuses/_commit_status.html.haml
@@ -9,14 +9,33 @@
     - else
       %strong Build ##{commit_status.id}
 
-  %td
-    = commit_status.ref
+    - if commit_status.show_warning?
+      %i.fa.fa-warning.text-warning
+
+  - if defined?(commit_sha) && commit_sha
+    %td
+      = link_to commit_status.short_sha, namespace_project_commit_path(@project.namespace, @project, commit_status.sha), class: "monospace"
 
   %td
-    = commit_status.stage
+    - if commit_status.ref
+      = link_to commit_status.ref, namespace_project_commits_path(@project.namespace, @project, commit_status.ref)
+    - else
+      .light none
+
+  - if defined?(runner) && runner
+    %td
+      - if commit_status.try(:runner)
+        = runner_link(commit_status.runner)
+      - else
+        .light none
+
+  - if defined?(stage) && stage
+    %td
+      = commit_status.stage
 
   %td
     = commit_status.name
+
     .pull-right
       - if commit_status.tags.any?
         - commit_status.tags.each do |tag|
@@ -33,7 +52,7 @@
 
   %td.timestamp
     - if commit_status.finished_at
-      %span #{time_ago_in_words commit_status.finished_at} ago
+      %span #{time_ago_with_tooltip(commit_status.finished_at)}
 
   - if defined?(coverage) && coverage
     %td.coverage
@@ -43,9 +62,10 @@
   %td
     .pull-right
       - if current_user && can?(current_user, :manage_builds, commit_status.gl_project)
-        - if commit_status.cancel_url
-          = link_to commit_status.cancel_url, title: 'Cancel' do
-            %i.fa.fa-remove.cred
+        - if commit_status.active?
+          - if commit_status.cancel_url
+            = link_to commit_status.cancel_url, method: :post, title: 'Cancel' do
+              %i.fa.fa-remove.cred
         - elsif defined?(allow_retry) && allow_retry && commit_status.retry_url
           = link_to commit_status.retry_url, method: :post, title: 'Retry' do
             %i.fa.fa-repeat
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index cddd5aa3a83ce29ca27091d82e829f4539110788..805be332e64510002ec7d9f7a754fe4a0a78387c 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -18,10 +18,10 @@
 
       .pull-right
         - if ci_commit
-          = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do
-            = ci_status_icon(ci_commit)
+          = render_ci_status(ci_commit)
           &nbsp;
-        = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
+        = clipboard_button
+        = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id", data: {clipboard_text: commit.id}
 
       .notes_count
         - if note_count > 0
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index a849bf84698d6adb505a86a421480038505704d2..f11a41cfd7bdbe5040e9dba75ff5d54b9c8f1777 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -12,7 +12,7 @@
       Branches
       %span.badge.js-totalbranch-count= @repository.branches.size
 
-  = nav_link(controller: :tags) do
+  = nav_link(controller: [:tags, :releases]) do
     = link_to namespace_project_tags_path(@project.namespace, @project) do
       Tags
       %span.badge.js-totaltags-count= @repository.tags.length
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index 4f1965bfb3961b14cc2ea5013d58fabb7cf42a82..e46bf1ab1e7c51a81b95c8084559d7924001f84e 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -3,7 +3,7 @@
 
 - diff_files = safe_diff_files(diffs)
 
-.gray-content-block.second-block
+.gray-content-block.second-block.oneline-block
   .inline-parallel-buttons
     .btn-group
       = inline_diff_btn
@@ -15,8 +15,8 @@
 
 .files
   - diff_files.each_with_index do |diff_file, index|
-    - diff_commit = commit_for_diff(diff_file.diff)
-    - blob = project.repository.blob_for_diff(diff_commit, diff_file.diff)
+    - diff_commit = commit_for_diff(diff_file)
+    - blob = project.repository.blob_for_diff(diff_commit, diff_file)
     - next unless blob
 
     = render 'projects/diffs/file', i: index, project: project,
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 9698921f6da74c326890fd06531b92266706f357..410ff6abb43f1f892f1560ed853a8ed65f88efe7 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -1,5 +1,5 @@
 .diff-file{id: "diff-#{i}", data: diff_file_html_data(project, diff_commit, diff_file)}
-  .diff-header{id: "file-path-#{hexdigest(diff_file.new_path || diff_file.old_path)}"}
+  .diff-header{id: "file-path-#{hexdigest(diff_file.file_path)}"}
     - if diff_file.diff.submodule?
       %span
         - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path)
@@ -38,7 +38,7 @@
       - else
         = render "projects/diffs/text_file", diff_file: diff_file, index: i
     - elsif blob.image?
-      - old_file = project.repository.prev_blob_for_diff(@commit, diff_file)
+      - old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file)
       = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i
     - else
       .nothing-here-block No preview for this file type
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 1882a82fba59c7aebcda86dad214508d91956906..afbf88b55073ed9bdcb37b348c3d345e5b18aa80 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -189,6 +189,21 @@
       - else
         .nothing-here-block Only the project owner can transfer a project
 
+      - if @project.forked?
+        - if can?(current_user, :remove_fork_project, @project)
+          = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_namespace_project_path(@project.namespace, @project), method: :delete, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f|
+            .panel.panel-default.panel.panel-danger
+              .panel-heading Remove fork relationship
+              .panel-body
+                %p
+                  This will remove the fork relationship to source project
+                  #{link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)}.
+                  %br
+                  %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source.
+                = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) }
+        - else
+          .nothing-here-block Only the project owner can remove the fork relationship.
+
       - if can?(current_user, :remove_project, @project)
         .panel.panel-default.panel.panel-danger
           .panel-heading Remove project
@@ -201,7 +216,8 @@
 
               = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
       - else
-        .nothing-here-block Only project owner can remove a project
+        .nothing-here-block Only the project owner can remove a project.
+
 
 .save-project-loader.hide
   .center
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index e06454fd14873fc0b6f4eb3e2db46f744f6518f8..c3858e78caddf4e44fc1e79a837bad28b4029e33 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -2,53 +2,56 @@
   - if current_user && can?(current_user, :download_code, @project)
     = render 'shared/no_ssh'
     = render 'shared/no_password'
-    
+
 = render "home_panel"
 
 .gray-content-block.center
   %h3.page-title
     The repository for this project is empty
-  %p
-    If you already have files you can push them using command line instructions below.
-    %br
-    Otherwise you can start with
-    = link_to "adding README", new_readme_path, class: 'underlined-link'
-    file to this project.
+  - if can?(current_user, :download_code, @project)
+    %p
+      If you already have files you can push them using command line instructions below.
+      %br
+      - if can?(current_user, :push_code, @project)
+        Otherwise you can start with
+        = link_to "adding README", new_readme_path, class: 'underlined-link'
+        file to this project.
 
-.prepend-top-20
-.empty_wrapper
-  %h3.page-title-empty
-    Command line instructions
-  %div.git-empty
-    %fieldset
-      %h5 Git global setup
-      %pre.light-well
-        :preserve
-          git config --global user.name "#{h git_user_name}"
-          git config --global user.email "#{h git_user_email}"
+- if can?(current_user, :download_code, @project)
+  .prepend-top-20
+  .empty_wrapper
+    %h3.page-title-empty
+      Command line instructions
+    %div.git-empty
+      %fieldset
+        %h5 Git global setup
+        %pre.light-well
+          :preserve
+            git config --global user.name "#{h git_user_name}"
+            git config --global user.email "#{h git_user_email}"
 
-    %fieldset
-      %h5 Create a new repository
-      %pre.light-well
-        :preserve
-          git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
-          cd #{h @project.path}
-          touch README.md
-          git add README.md
-          git commit -m "add README"
-          git push -u origin master
+      %fieldset
+        %h5 Create a new repository
+        %pre.light-well
+          :preserve
+            git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
+            cd #{h @project.path}
+            touch README.md
+            git add README.md
+            git commit -m "add README"
+            git push -u origin master
 
-    %fieldset
-      %h5 Existing folder or Git repository
-      %pre.light-well
-        :preserve
-          cd existing_folder
-          git init
-          git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
-          git add .
-          git commit
-          git push -u origin master
+      %fieldset
+        %h5 Existing folder or Git repository
+        %pre.light-well
+          :preserve
+            cd existing_folder
+            git init
+            git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
+            git add .
+            git commit
+            git push -u origin master
 
-      - if can? current_user, :remove_project, @project
-        .prepend-top-20
-          = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
+        - if can? current_user, :remove_project, @project
+          .prepend-top-20
+            = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index bbfaf422a82ea2be50dc9d37899492ebe0549435..e0d06a14bf44ee79d39d885ac4a21a341217c11d 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -1,4 +1,4 @@
-%ul.nav.nav-tabs
+%ul.center-top-menu
   = nav_link(action: :show) do
     = link_to 'Contributors', namespace_project_graph_path
   = nav_link(action: :commits) do
diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml
index 4f69cc64f7c9acf616984698ddd1c3672123a649..6fa77cc10c663d2c160e74ccada3ca980b48ab45 100644
--- a/app/views/projects/graphs/ci.html.haml
+++ b/app/views/projects/graphs/ci.html.haml
@@ -1,7 +1,16 @@
 - page_title "Continuous Integration", "Graphs"
 = render "header_title"
 = render 'head'
+.gray-content-block.append-bottom-default
+  .oneline
+    A collection of graphs for Continuous Integration
+
 #charts.ci-charts
+  .row
+    .col-md-6
+      = render 'projects/graphs/ci/overall'
+    .col-md-6
+      = render 'projects/graphs/ci/build_times'
+
+  %hr
   = render 'projects/graphs/ci/builds'
-  = render 'projects/graphs/ci/build_times'
-= render 'projects/graphs/ci/overall'
diff --git a/app/views/projects/graphs/ci/_build_times.haml b/app/views/projects/graphs/ci/_build_times.haml
index c3c2f5724148aa6e2b4af15ad6784882e00e2b1f..c58223fd39e644c66e6d9f71e9a9dd97ab2e82fa 100644
--- a/app/views/projects/graphs/ci/_build_times.haml
+++ b/app/views/projects/graphs/ci/_build_times.haml
@@ -1,21 +1,22 @@
-%fieldset
-  %legend
+%div
+  %p.light
     Commit duration in minutes for last 30 commits
 
-  %canvas#build_timesChart.padded{width: 800, height: 300}
+  %canvas#build_timesChart{height: 200}
 
 :javascript
   var data = {
     labels : #{@charts[:build_times].labels.to_json},
     datasets : [
       {
-        fillColor : "#4A3",
-        strokeColor : "rgba(151,187,205,1)",
-        pointColor : "rgba(151,187,205,1)",
-        pointStrokeColor : "#fff",
+        fillColor : "rgba(220,220,220,0.5)",
+        strokeColor : "rgba(220,220,220,1)",
+        barStrokeWidth: 1,
+        barValueSpacing: 1,
+        barDatasetSpacing: 1,
         data : #{@charts[:build_times].build_times.to_json}
       }
     ]
   }
   var ctx = $("#build_timesChart").get(0).getContext("2d");
-  new Chart(ctx).Line(data,{"scaleOverlay": true});
+  new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false});
diff --git a/app/views/projects/graphs/ci/_builds.haml b/app/views/projects/graphs/ci/_builds.haml
index 1b0039fb8346e6f98e31fb438ba324ddacb35c79..8fca07114fad7d8577930924c7d4c7b955c68ee7 100644
--- a/app/views/projects/graphs/ci/_builds.haml
+++ b/app/views/projects/graphs/ci/_builds.haml
@@ -1,20 +1,30 @@
-%fieldset
-  %legend
-    Builds chart for last week
-    (#{date_from_to(Date.today - 7.days, Date.today)})
+%h4 Build charts
+%p
+  &nbsp;
+  %span.cgreen
+    = icon("circle")
+    success
+  &nbsp;
+  %span.cgray
+    = icon("circle")
+    all
 
-  %canvas#weekChart.padded{width: 800, height: 200}
+.prepend-top-default
+  %p.light
+    Builds for last week
+    (#{date_from_to(Date.today - 7.days, Date.today)})
+  %canvas#weekChart{height: 200}
 
-%fieldset
-  %legend
-    Builds chart for last month
+.prepend-top-default
+  %p.light
+    Builds for last month
     (#{date_from_to(Date.today - 30.days, Date.today)})
+  %canvas#monthChart{height: 200}
 
-  %canvas#monthChart.padded{width: 800, height: 300}
-
-%fieldset
-  %legend Builds chart for last year
-  %canvas#yearChart.padded{width: 800, height: 400}
+.prepend-top-default
+  %p.light
+    Builds for last year
+  %canvas#yearChart.padded{height: 250}
 
 - [:week, :month, :year].each do |scope|
   :javascript
@@ -22,20 +32,20 @@
       labels : #{@charts[scope].labels.to_json},
       datasets : [
         {
-          fillColor : "rgba(220,220,220,0.5)",
-          strokeColor : "rgba(220,220,220,1)",
-          pointColor : "rgba(220,220,220,1)",
+          fillColor : "#7f8fa4",
+          strokeColor : "#7f8fa4",
+          pointColor : "#7f8fa4",
           pointStrokeColor : "#EEE",
           data : #{@charts[scope].total.to_json}
         },
         {
-          fillColor : "#4A3",
-          strokeColor : "rgba(151,187,205,1)",
-          pointColor : "rgba(151,187,205,1)",
+          fillColor : "#44aa22",
+          strokeColor : "#44aa22",
+          pointColor : "#44aa22",
           pointStrokeColor : "#fff",
           data : #{@charts[scope].success.to_json}
         }
       ]
     }
     var ctx = $("##{scope}Chart").get(0).getContext("2d");
-    new Chart(ctx).Line(data,{"scaleOverlay": true});
+    new Chart(ctx).Line(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false});
diff --git a/app/views/projects/graphs/ci/_overall.haml b/app/views/projects/graphs/ci/_overall.haml
index 9550d71947195ba2525559360f2d0edf5fc8db3b..cf4285a2671dc0d505834e5294f0471f6c040533 100644
--- a/app/views/projects/graphs/ci/_overall.haml
+++ b/app/views/projects/graphs/ci/_overall.haml
@@ -1,22 +1,20 @@
 - ci_project = @project.gitlab_ci_project
-%fieldset
-  %legend Overall
-  %p
+%h4 Overall stats
+%ul
+  %li
     Total:
     %strong= pluralize ci_project.builds.count(:all), 'build'
-  %p
+  %li
     Successful:
     %strong= pluralize ci_project.builds.success.count(:all), 'build'
-  %p
+  %li
     Failed:
     %strong= pluralize ci_project.builds.failed.count(:all), 'build'
-
-  %p
+  %li
     Success ratio:
     %strong
       #{success_ratio(ci_project.builds.success, ci_project.builds.failed)}%
-
-  %p
+  %li
     Commits covered:
     %strong
       = ci_project.commits.count(:all)
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index 112be875b6b918ad9a635e9fc1faa0abd50594d5..fc465ab273bd96bb1d8a838af53755546f470b5d 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -1,9 +1,13 @@
 - page_title "Commits", "Graphs"
 = render "header_title"
-.tree-ref-holder
-  = render 'shared/ref_switcher', destination: 'graphs_commits'
 = render 'head'
 
+.gray-content-block.append-bottom-default
+  .tree-ref-holder
+    = render 'shared/ref_switcher', destination: 'graphs_commits'
+  %ul.breadcrumb.repo-breadcrumb
+    = commits_breadcrumbs
+
 %p.lead
   Commit statistics for
   %strong #{@ref}
@@ -45,26 +49,24 @@
         Commits per weekday
       %canvas#weekday-chart
 
-:coffeescript
-  responsiveChart = (selector, data) ->
-    options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false }
-
-    # get selector by context
-    ctx = selector.get(0).getContext("2d")
-    # pointing parent container to make chart.js inherit its width
-    container = $(selector).parent()
-
-    generateChart = ->
-      selector.attr('width', $(container).width())
-      new Chart(ctx).Bar(data, options)
-
-    # enabling auto-resizing
-    $(window).resize( generateChart )
-
-    generateChart()
+:javascript
+  var responsiveChart = function (selector, data) {
+    var options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false };
+    // get selector by context
+    var ctx = selector.get(0).getContext("2d");
+    // pointing parent container to make chart.js inherit its width
+    var container = $(selector).parent();
+    var generateChart = function() {
+      selector.attr('width', $(container).width());
+      return new Chart(ctx).Bar(data, options);
+    };
+    // enabling auto-resizing
+    $(window).resize(generateChart);
+    return generateChart();
+  };
 
-  chartData = (keys, values) ->
-    data = {
+  var chartData = function (keys, values) {
+    var data = {
       labels : keys,
       datasets : [{
         fillColor : "rgba(220,220,220,0.5)",
@@ -74,13 +76,15 @@
         barDatasetSpacing: 1,
         data : values
       }]
-  }
+    };
+    return data;
+  };
 
-  hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json})
-  responsiveChart($('#hour-chart'), hourData)
+  var hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json});
+  responsiveChart($('#hour-chart'), hourData);
 
-  dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json})
-  responsiveChart($('#weekday-chart'), dayData)
+  var dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json});
+  responsiveChart($('#weekday-chart'), dayData);
 
-  monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json})
-  responsiveChart($('#month-chart'), monthData)
+  var monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json});
+  responsiveChart($('#month-chart'), monthData);
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index bd342911e49d04c056a6dc0a0f1ebc4931e393f3..882e7d6b6ee42c320cd529563af8a845d15215e4 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,9 +1,13 @@
 - page_title "Contributors", "Graphs"
 = render "header_title"
-.tree-ref-holder
-  = render 'shared/ref_switcher', destination: 'graphs'
 = render 'head'
 
+.gray-content-block.append-bottom-default
+  .tree-ref-holder
+    = render 'shared/ref_switcher', destination: 'graphs'
+  %ul.breadcrumb.repo-breadcrumb
+    = commits_breadcrumbs
+
 .loading-graph
   .center
     %h3.page-title
@@ -24,18 +28,21 @@
 
 
 
-:coffeescript
-  $.ajax
+:javascript
+  $.ajax({
     type: "GET",
     url: location.href,
-    success: (data) ->
-      graph = new ContributorsStatGraph()
-      graph.init(data)
+    dataType: "json",
+    success: function (data) {
+      var graph = new ContributorsStatGraph();
+      graph.init(data);
 
-      $("#brush_change").change ->
-        graph.change_date_header()
-        graph.redraw_authors()
+      $("#brush_change").change(function(){
+        graph.change_date_header();
+        graph.redraw_authors();
+      });
 
       $(".stat-graph").fadeIn();
       $(".loading-graph").hide();
-    dataType: "json"
+    }
+  });
diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml
index 85dbfd6786258ffc7dbf9b4daa554cccc5a1abbc..3702aeaecba0c396c8ab43d4968cc9cf6e249d95 100644
--- a/app/views/projects/hooks/index.html.haml
+++ b/app/views/projects/hooks/index.html.haml
@@ -19,7 +19,7 @@
       = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json'
   .form-group
     = f.label :url, "Trigger", class: 'control-label'
-    .col-sm-10
+    .col-sm-10.prepend-top-10
       %div
         = f.check_box :push_events, class: 'pull-left'
         .prepend-left-20
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index f8f2e192e291f20a8960862def746e375953f2c4..92a87690c54992961bdb9bc97b72d39ff97d998b 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -17,6 +17,6 @@
         This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
         %br
         The import will time out after 4 minutes. For big repositories, use a clone/push combination.
-        For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
+        For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}
   .form-actions
     = f.submit 'Start import', class: "btn btn-create", tabindex: 4
diff --git a/app/views/projects/issues/_closed_by_box.html.haml b/app/views/projects/issues/_closed_by_box.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..aef352029d06d5425d2bee39b108af321c49dfb5
--- /dev/null
+++ b/app/views/projects/issues/_closed_by_box.html.haml
@@ -0,0 +1,3 @@
+.issue-closed-by-widget
+  = icon('check')
+  This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests.sort))} is accepted.
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index d4a98eca4735344fd3ee529a533c5e5f049ee280..c5fd863ae99eacf0ed5c83eb2e27fb3eb28527be 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -17,8 +17,10 @@
         - @participants.each do |participant|
           = link_to_member(@project, participant, name: false, size: 24)
     .col-md-3
-      %span.slead.has_tooltip{title: 'Cross-project reference'}
-        = cross_project_reference(@project, @issue)
+      .input-group.cross-project-reference
+        %span.slead.has_tooltip{title: 'Cross-project reference'}
+          = cross_project_reference(@project, @issue)
+        = clipboard_button
 
 .row
   %section.col-md-9
diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml
index a3399c57aa2af2dc889a3daa7a45f4297b214dda..ca5b1a8386d63497f2a8018a216c6069001ac0cb 100644
--- a/app/views/projects/issues/_issues.html.haml
+++ b/app/views/projects/issues/_issues.html.haml
@@ -5,8 +5,9 @@
       .nothing-here-block No issues to show
 
 - if @issues.present?
-  .pull-right
-    %span.issue_counter #{@issues.total_count}
-    issues for this filter
+  .issuable-filter-count
+    %span.pull-right
+      = @issues.total_count
+      issues for this filter
 
   = paginate @issues, theme: "gitlab"
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 5cb814c9ea84cb29579c6479e9d1b8f8010c2e1e..f01bf2505dad68e7e8b91463af86244f1b5a5bba 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -46,6 +46,7 @@
                 = markdown(@issue.description)
             %textarea.hidden.js-task-list-field
               = @issue.description
-
+  - if @closed_by_merge_requests.present?
+    = render 'projects/issues/closed_by_box'
   .issue-discussion
     = render 'projects/issues/discussion'
diff --git a/app/views/projects/labels/destroy.js.haml b/app/views/projects/labels/destroy.js.haml
index 1b4c83ab0979ff45905f9d5d17171015e80dc66d..d59563b122aefb78357d98ec2cb153f0ef91b5ca 100644
--- a/app/views/projects/labels/destroy.js.haml
+++ b/app/views/projects/labels/destroy.js.haml
@@ -1,2 +1,2 @@
 - if @project.labels.size == 0
-  $('.labels').load(document.URL + ' .light-well').hide().fadeIn(1000)
+  $('.labels').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000)
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 97175f8232b487a28f1b63b33b63bb690265d687..fb784ee5f4f4f686432ca1cf62c6f8b88d71d0ec 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -14,8 +14,8 @@
       = render @labels
     = paginate @labels, theme: 'gitlab'
   - else
-    .light-well
+    .nothing-here-block
       - if can? current_user, :admin_label, @project
-        .nothing-here-block Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels
+        Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels
       - else
-        .nothing-here-block No labels created
+        No labels created
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index 38e66c3828bb78138b610b4e158fe67512257d77..7e60782ff5b699cb8d70ea5d68a147a5909fd006 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -7,7 +7,7 @@
 
 = render 'shared/show_aside'
 
-.gray-content-block.second-block
+.gray-content-block.second-block.oneline-block
   .row
     .col-md-9
       .votes-holder.pull-right
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 25e4e8ba80d7aab6bde96b47f3fafed389a30792..c5234c0618c4d06250ad6b6032cae58cb8bcb0c0 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -1,3 +1,4 @@
+- ci_commit = merge_request.ci_commit
 %li{ class: mr_css_classes(merge_request) }
   .merge-request-title
     %span.merge-request-title-text
@@ -6,6 +7,8 @@
       - merge_request.labels.each do |label|
         = link_to_label(label, project: merge_request.project)
     .pull-right.light
+      - if ci_commit
+        = render_ci_status(ci_commit)
       - if merge_request.merged?
         %span
           %i.fa.fa-check
@@ -38,6 +41,11 @@
       %span
         %i.fa.fa-clock-o
         = merge_request.milestone.title
+    - if merge_request.target_project.default_branch != merge_request.target_branch
+      &nbsp;
+      %span
+        %i.fa.fa-code-fork
+        = merge_request.target_branch
     - if merge_request.tasks?
       %span.task-status
         = merge_request.task_status
diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml
index d86707b3d97eaa12b2edc10ad4fa35b96acba366..0af970e4b922ecc3d78ce4683c55f087afe42f1c 100644
--- a/app/views/projects/merge_requests/_merge_requests.html.haml
+++ b/app/views/projects/merge_requests/_merge_requests.html.haml
@@ -5,8 +5,10 @@
       .nothing-here-block No merge requests to show
 
 - if @merge_requests.present?
-  .pull-right
-    %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter
+  .issuable-filter-count
+    %span.pull-right
+      = @merge_requests.total_count
+      merge requests for this filter
 
   = paginate @merge_requests, theme: "gitlab"
 
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index 452006162dbf44872e3afff78f9df87ecfd47162..d9eff1f9320aeaeb0a779cc4fad575158ca4d71e 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -77,12 +77,13 @@
   });
 
 
-:coffeescript
-
-  $(".merge-request-form").on 'submit', ->
-    if $("#merge_request_source_branch").val() is "" or $('#merge_request_target_branch').val() is ""
-      $(".mr-compare-errors").html("You must select source and target branch to proceed")
-      $(".mr-compare-errors").fadeIn()
-      event.preventDefault()
-      return
+:javascript
+  $(".merge-request-form").on('submit', function () {
+    if ($("#merge_request_source_branch").val() === "" || $('#merge_request_target_branch').val() === "") {
+      $(".mr-compare-errors").html("You must select source and target branch to proceed");
+      $(".mr-compare-errors").fadeIn();
+      event.preventDefault();
+      return;
+    }
+  });
 
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index e7ac7a0eaa45df35e92666361cd73347ae9326c1..eeaa72ed21bebf3e15a4dc7ea50d3abbcfec3e02 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -36,7 +36,8 @@
     - if @merge_request.open? && @merge_request.can_be_merged?
       .light.append-bottom-20
         You can also accept this merge request manually using the
-        = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
+        = succeed '.' do
+          = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
 
   - if @commits.present?
     %ul.merge-request-tabs
diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml
index a71b181a6a5c44af70df4293c44183f104a5ded8..478054db517a5d3eaf2530608f769104fb58adc6 100644
--- a/app/views/projects/merge_requests/show/_commits.html.haml
+++ b/app/views/projects/merge_requests/show/_commits.html.haml
@@ -1 +1,5 @@
+.gray-content-block.second-block.oneline-block
+  = icon("sort-amount-desc")
+  Most recent commits displayed first
+
 = render "projects/commits/commits", project: @merge_request.project
diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
index f18cf96c17df923a1650878ee877ac4593d80c70..98f0357ce4ea4699632e01c2a6e469a68aa1eadd 100644
--- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml
+++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml
@@ -3,11 +3,12 @@
     .modal-content
       .modal-header
         %a.close{href: "#", "data-dismiss" => "modal"} ×
-        %h3 Check out, review and merge locally
+        %h3 Check out, review, and merge locally
       .modal-body
         %p
-          %strong Step 1. 
+          %strong Step 1.
           Fetch and check out the branch for this merge request
+        = clipboard_button
         %pre.dark
           - if @merge_request.for_fork?
             :preserve
@@ -24,6 +25,7 @@
         %p
           %strong Step 3.
           Merge the branch and fix any conflicts that come up
+        = clipboard_button
         %pre.dark
           - if @merge_request.for_fork?
             :preserve
@@ -36,6 +38,7 @@
         %p
           %strong Step 4.
           Push the result of the merge to GitLab
+        = clipboard_button
         %pre.dark
           :preserve
             git push origin #{h @merge_request.target_branch}
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index 68dda1424cfa9b8d8aebe24922c8c671bdc074d5..ba5ad22bca750a83285150c78bb4ee6f2e8a3db4 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -1,44 +1,44 @@
-- if @merge_request.has_ci?
-  - ci_commit = @merge_request.source_project.ci_commit(@merge_request.source_sha)
-  - if ci_commit
-    - status = ci_commit.status
-    .mr-widget-heading
-      .ci_widget{class: "ci-#{status}"}
-        = ci_status_icon(ci_commit)
+- ci_commit = @merge_request.ci_commit
+- if ci_commit
+  - status = ci_commit.status
+  .mr-widget-heading
+    .ci_widget{class: "ci-#{status}"}
+      = ci_status_icon(ci_commit)
+      %span CI build #{status}
+      for #{@merge_request.last_commit_short_sha}.
+      %span.ci-coverage
+      = link_to "View build details", ci_status_path(ci_commit)
+
+- elsif @merge_request.has_ci?
+  - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
+  - # Remove in later versions when services like Jenkins will set CI status via Commit status API
+  .mr-widget-heading
+    - [:success, :skipped, :canceled, :failed, :running, :pending].each do |status|
+      .ci_widget{class: "ci-#{status}", style: "display:none"}
+        - if status == :success
+          - status = "passed"
+          = icon("check-circle")
+        - else
+          = icon("circle")
         %span CI build #{status}
         for #{@merge_request.last_commit_short_sha}.
         %span.ci-coverage
-        = link_to "View build details", ci_status_path(ci_commit)
-
-  - else
-    - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
-    - # Remove in later versions when services like Jenkins will set CI status via Commit status API
-    .mr-widget-heading
-      - [:success, :skipped, :canceled, :failed, :running, :pending].each do |status|
-        .ci_widget{class: "ci-#{status}", style: "display:none"}
-          - if status == :success
-            - status = "passed"
-            = icon("check-circle")
-          - else
-            = icon("circle")
-          %span CI build #{status}
-          for #{@merge_request.last_commit_short_sha}.
-          %span.ci-coverage
-          - if ci_build_details_path(@merge_request)
-            = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
+        - if ci_build_details_path(@merge_request)
+          = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
 
-      .ci_widget
-        = icon("spinner spin")
-        Checking CI status for #{@merge_request.last_commit_short_sha}&hellip;
+    .ci_widget
+      = icon("spinner spin")
+      Checking CI status for #{@merge_request.last_commit_short_sha}&hellip;
 
-      .ci_widget.ci-not_found{style: "display:none"}
-        = icon("times-circle")
-        Could not find CI status for #{@merge_request.last_commit_short_sha}.
+    .ci_widget.ci-not_found{style: "display:none"}
+      = icon("times-circle")
+      Could not find CI status for #{@merge_request.last_commit_short_sha}.
 
-      .ci_widget.ci-error{style: "display:none"}
-        = icon("times-circle")
-        Could not connect to the CI server. Please check your settings and try again.
+    .ci_widget.ci-error{style: "display:none"}
+      = icon("times-circle")
+      Could not connect to the CI server. Please check your settings and try again.
 
-    :coffeescript
-      $ ->
-        merge_request_widget.getCiStatus()
+  :javascript
+    $(function() {
+      merge_request_widget.getCiStatus();
+    });
diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml
index f223f687defbc42965249a821e85b79c92efe48c..a788fcea23f4daaf4ba91b5ed04084aa9e0f681d 100644
--- a/app/views/projects/merge_requests/widget/_merged.html.haml
+++ b/app/views/projects/merge_requests/widget/_merged.html.haml
@@ -15,7 +15,7 @@
 
       - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch)
         .remove_source_branch_widget
-          %p 
+          %p
             = succeed '.' do
               The changes were merged into
               %span.label-branch= @merge_request.target_branch
@@ -25,7 +25,7 @@
             Remove Source Branch
 
         .remove_source_branch_widget.failed.hide
-          %p 
+          %p
             Failed to remove source branch '#{@merge_request.source_branch}'.
 
         .remove_source_branch_in_progress.hide
@@ -33,17 +33,20 @@
             = icon('spinner spin')
             Removing source branch '#{@merge_request.source_branch}'. Please wait. This page will be automatically reload.
 
-        :coffeescript
-          $('.remove_source_branch').on 'click', ->
-            $('.remove_source_branch_widget').hide()
-            $('.remove_source_branch_in_progress').show()
-
-          $(".remove_source_branch").on "ajax:success", (e, data, status, xhr) ->
-            location.reload()
-
-          $(".remove_source_branch").on "ajax:error", (e, data, status, xhr) ->
-            $('.remove_source_branch_widget').hide()
-            $('.remove_source_branch_in_progress').hide()
-            $('.remove_source_branch_widget.failed').show()
+        :javascript
+          $('.remove_source_branch').on('click', function() {
+            $('.remove_source_branch_widget').hide();
+            $('.remove_source_branch_in_progress').show();
+          });
+
+          $(".remove_source_branch").on("ajax:success", function (e, data, status, xhr) {
+            location.reload();
+          });
+
+          $(".remove_source_branch").on("ajax:error", function (e, data, status, xhr) {
+            $('.remove_source_branch_widget').hide();
+            $('.remove_source_branch_in_progress').hide();
+            $('.remove_source_branch_widget.failed').show();
+          });
 
 
diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml
index 613525437abf42110df95c044c258b3480794819..9b31014b581768aea12f28e6fb4d0068816aad20 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -1,8 +1,10 @@
+- status_class = @merge_request.ci_commit ? " ci-#{@merge_request.ci_commit.status}" : nil
+
 = form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f|
   = hidden_field_tag :authenticity_token, form_authenticity_token
   .accept-merge-holder.clearfix.js-toggle-container
     .accept-action
-      = f.button class: "btn btn-create accept_merge_request" do
+      = f.button class: "btn btn-create accept_merge_request#{status_class}" do
         Accept Merge Request
     - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
       .accept-control.checkbox
@@ -18,8 +20,9 @@
           text: @merge_request.merge_commit_message,
           rows: 14, hint: true
 
-  :coffeescript
-    $('.accept-mr-form').on 'ajax:before', ->
-      btn = $('.accept_merge_request')
-      btn.disable()
-      btn.html("<i class='fa fa-spinner fa-spin'></i> Merge in progress")
+  :javascript
+    $('.accept-mr-form').on('ajax:before', function() {
+      var btn = $('.accept_merge_request');
+      btn.disable();
+      btn.html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
+    });
diff --git a/app/views/projects/merge_requests/widget/open/_check.html.haml b/app/views/projects/merge_requests/widget/open/_check.html.haml
index b6b8974297e62534fa7d2a443cced88b0b78bb39..e16878ba51383bab3bfc837d4a88ed4e26a0c2a3 100644
--- a/app/views/projects/merge_requests/widget/open/_check.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_check.html.haml
@@ -2,6 +2,8 @@
   = icon("spinner spin")
   Checking ability to merge automatically&hellip;
 
-:coffeescript
-  $ ->
-    merge_request_widget.getMergeStatus()
+:javascript
+  $(function() {
+    merge_request_widget.getMergeStatus();
+  });
+
diff --git a/app/views/projects/milestones/_issue.html.haml b/app/views/projects/milestones/_issue.html.haml
index 88fccfe4981c4e665c299729080f7b37fa2a85ab..133d802aaca1646f60211d3843fa0491b950f7d6 100644
--- a/app/views/projects/milestones/_issue.html.haml
+++ b/app/views/projects/milestones/_issue.html.haml
@@ -1,7 +1,7 @@
 %li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) }
   .pull-right.assignee-icon
     - if issue.assignee
-      = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16", alt: ''
   %span
     = link_to [@project.namespace.becomes(Namespace), @project, issue] do
       %span.cgray ##{issue.iid}
diff --git a/app/views/projects/milestones/_merge_request.html.haml b/app/views/projects/milestones/_merge_request.html.haml
index 0d7a118569a8943037af86bbadf8dd745cb799e8..a1033607c5de0fb94ede75943e56b1a09b452e57 100644
--- a/app/views/projects/milestones/_merge_request.html.haml
+++ b/app/views/projects/milestones/_merge_request.html.haml
@@ -5,4 +5,4 @@
     = link_to_gfm merge_request.title, [@project.namespace.becomes(Namespace), @project, merge_request], title: merge_request.title
   .pull-right.assignee-icon
     - if merge_request.assignee
-      = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: ''
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 4eeb0621e526c32f1d824679c9ae115982760b88..3a898dfbcfd1f2406da96d2afc8e7c71e4cfd77d 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -104,7 +104,7 @@
       - @users.each do |user|
         %li
           = link_to user, title: user.name, class: "darken" do
-            = image_tag avatar_icon(user.email, 32), class: "avatar s32"
+            = image_tag avatar_icon(user, 32), class: "avatar s32"
             %strong= truncate(user.name, lenght: 40)
             %br
             %small.cgray= user.username
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index daab2326bc7615d88c6a43ed6ae4b8f5fac58397..a02c12f06a8763bd71ecba7facc92a6b28aaa424 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -124,9 +124,11 @@
       Creating project &amp; repository.
     %p Please wait a moment, this page will automatically refresh when ready.
 
-:coffeescript
-  $('.how_to_import_link').bind 'click', (e) ->
-    e.preventDefault()
-    import_modal = $(this).next(".modal").show()
-  $('.modal-header .close').bind 'click', ->
-    $(".modal").hide()
+:javascript
+  $('.how_to_import_link').bind('click', function (e) {
+    e.preventDefault();
+    var import_modal = $(this).next(".modal").show();
+  });
+  $('.modal-header .close').bind('click', function() {
+    $(".modal").hide();
+  });
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 1638ad6891ae54e085338d1248e48a81f281c945..5d184730796f9112b6f4db44967b0aa934a089ba 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -1,8 +1,8 @@
 %li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)], data: { discussion: note.discussion_id } }
   .timeline-entry-inner
     .timeline-icon
-      = link_to user_path(note.author) do
-        = image_tag avatar_icon(note.author_email), class: 'avatar s40', alt: ''
+      %a{href: user_path(note.author)}
+        %img.avatar.s40{src: avatar_icon(note.author), alt: ''}
     .timeline-content
       .note-header
         - if note_editable?(note)
@@ -25,7 +25,7 @@
           = '@' + note.author.username
 
         %span.note-last-update
-          = link_to "##{dom_id(note)}", name: dom_id(note), title: "Link here" do
+          %a{name: dom_id(note), href: "##{dom_id(note)}", title: 'Link here'}
             = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago')
           - if note.updated_at != note.created_at
             %span
diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml
index 860a997cff84c6f7fb10c69a6f8e3caa06face39..76c46d1d8067a7fe7dc855fa09a342c40260dbc6 100644
--- a/app/views/projects/project_members/_project_member.html.haml
+++ b/app/views/projects/project_members/_project_member.html.haml
@@ -4,7 +4,7 @@
 %li{class: "#{dom_class(member)} js-toggle-container project_member_row access-#{member.human_access.downcase}", id: dom_id(member)}
   %span.list-item-name
     - if member.user
-      = image_tag avatar_icon(user.email, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(user, 16), class: "avatar s16", alt: ''
       %strong
         = link_to user.name, user_path(user)
       %span.cgray= user.username
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 82809bec5b81f49754c11d384e0e26e491b96e34..9fc4be583cc3fa6740a9a66a8d9579ee5f96826b 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -29,7 +29,8 @@
 - if @group
   = render "group_members", members: @group_members
 
-:coffeescript
-  $('form.member-search-form').on 'submit', (event) ->
-    event.preventDefault()
-    Turbolinks.visit @.action + '?' + $(@).serialize()
+:javascript
+  $('form.member-search-form').on('submit', function (event) {
+    event.preventDefault();
+    Turbolinks.visit(this.action + '?' + $(this).serialize());
+  });
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
index bb49f4de8736ce2422f605b09c67824ac4ef0040..f68449b186394b54ec952aff3f492c06680621cf 100644
--- a/app/views/projects/protected_branches/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/_branches_list.html.haml
@@ -1,34 +1,35 @@
 - unless @branches.empty?
   %br
   %h4 Already Protected:
-  %table.table.protected-branches-list
-    %thead
-      %tr.no-border
-        %th Branch
-        %th Developers can push
-        %th Last commit
-        %th
+  .table-holder
+    %table.table.protected-branches-list
+      %thead
+        %tr.no-border
+          %th Branch
+          %th Developers can push
+          %th Last commit
+          %th
 
-    %tbody
-      - @branches.each do |branch|
-        - @url = namespace_project_protected_branch_path(@project.namespace, @project, branch)
-        %tr
-          %td
-            = link_to namespace_project_commits_path(@project.namespace, @project, branch.name) do
-              %strong= branch.name
-              - if @project.root_ref?(branch.name)
-                %span.label.label-info default
+      %tbody
+        - @branches.each do |branch|
+          - @url = namespace_project_protected_branch_path(@project.namespace, @project, branch)
+          %tr
             %td
-              = check_box_tag "developers_can_push", branch.id, branch.developers_can_push, "data-url" => @url
-            %td
-              - if commit = branch.commit
-                = link_to namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id' do
-                  = commit.short_id
-                &middot;
-                #{time_ago_with_tooltip(commit.committed_date)}
-              - else
-                (branch was removed from repository)
-            %td
-              .pull-right
-                - if can? current_user, :admin_project, @project
-                  = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm"
+              = link_to namespace_project_commits_path(@project.namespace, @project, branch.name) do
+                %strong= branch.name
+                - if @project.root_ref?(branch.name)
+                  %span.label.label-info default
+              %td
+                = check_box_tag "developers_can_push", branch.id, branch.developers_can_push, "data-url" => @url
+              %td
+                - if commit = branch.commit
+                  = link_to namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id' do
+                    = commit.short_id
+                  &middot;
+                  #{time_ago_with_tooltip(commit.committed_date)}
+                - else
+                  (branch was removed from repository)
+              %td
+                .pull-right
+                  - if can? current_user, :admin_project, @project
+                    = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm"
diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..e7db09cdaa9153b3ec8957ce0950fb8993b9b4c5
--- /dev/null
+++ b/app/views/projects/releases/edit.html.haml
@@ -0,0 +1,20 @@
+- page_title "Edit", @tag.name, "Tags"
+= render "projects/commits/header_title"
+= render "projects/commits/head"
+
+.gray-content-block
+  .oneline
+    .title
+      Release notes for tag
+      %strong #{@tag.name}
+
+.prepend-top-default
+  = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form' }) do |f|
+    = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
+      = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit'
+      = render 'projects/notes/hints'
+      .error-alert
+      .prepend-top-default
+        = f.submit 'Save changes', class: 'btn btn-save'
+        = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel"
+
diff --git a/app/views/projects/remove_fork.js.haml b/app/views/projects/remove_fork.js.haml
new file mode 100644
index 0000000000000000000000000000000000000000..17b9fecfeb162f8f3ac88523a7fbe336c4d46516
--- /dev/null
+++ b/app/views/projects/remove_fork.js.haml
@@ -0,0 +1,2 @@
+:plain
+    location.href = "#{edit_namespace_project_path(@project.namespace, @project)}";
diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml
index 66851d3831638654fdb8a74a29ad6e251b5b5217..dde9e448cb92194e2c7efd70c4914712c3c59d21 100644
--- a/app/views/projects/runners/edit.html.haml
+++ b/app/views/projects/runners/edit.html.haml
@@ -1,3 +1,5 @@
+- page_title "Edit", "#{@runner.description} ##{@runner.id}", "Runners"
+
 %h4 Runner ##{@runner.id}
 %hr
 = form_for @runner, url: runner_path(@runner), html: { class: 'form-horizontal' } do |f|
diff --git a/app/views/projects/runners/index.html.haml b/app/views/projects/runners/index.html.haml
index 529fb9c296d4a65e9748abc44a68aa909e36e2e1..315afe4a764506b1e66e553d6accbc17b26cb68d 100644
--- a/app/views/projects/runners/index.html.haml
+++ b/app/views/projects/runners/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Runners"
 .light
   %p
     A 'runner' is a process which runs a build.
diff --git a/app/views/projects/runners/show.html.haml b/app/views/projects/runners/show.html.haml
index ffec495f85a132a0a9d69463bb7690fe97e0f49e..5bf4c09ca25e09e548fa3add532f4f57b36cb4c9 100644
--- a/app/views/projects/runners/show.html.haml
+++ b/app/views/projects/runners/show.html.haml
@@ -1,64 +1,66 @@
-= content_for :title do
-  %h3.project-title
-    Runner ##{@runner.id}
-    .pull-right
-      - if @runner.shared?
-        %span.runner-state.runner-state-shared
-          Shared
-      - else
-        %span.runner-state.runner-state-specific
-          Specific
+- page_title "#{@runner.description} ##{@runner.id}", "Runners"
 
-%table.table
-  %thead
+%h3.page-title
+  Runner ##{@runner.id}
+  .pull-right
+    - if @runner.shared?
+      %span.runner-state.runner-state-shared
+        Shared
+    - else
+      %span.runner-state.runner-state-specific
+        Specific
+
+.table-holder
+  %table.table
+    %thead
+      %tr
+        %th Property Name
+        %th Value
+    %tr
+      %td
+        Tags
+      %td
+        - @runner.tag_list.each do |tag|
+          %span.label.label-primary
+            = tag
+    %tr
+      %td
+        Name
+      %td
+        = @runner.name
+    %tr
+      %td
+        Version
+      %td
+        = @runner.version
+    %tr
+      %td
+        Revision
+      %td
+        = @runner.revision
+    %tr
+      %td
+        Platform
+      %td
+        = @runner.platform
+    %tr
+      %td
+        Architecture
+      %td
+        = @runner.architecture
+    %tr
+      %td
+        Description
+      %td
+        = @runner.description
     %tr
-      %th Property Name
-      %th Value
-  %tr
-    %td
-      Tags
-    %td
-      - @runner.tag_list.each do |tag|
-        %span.label.label-primary
-          = tag
-  %tr
-    %td
-      Name
-    %td
-      = @runner.name
-  %tr
-    %td
-      Version
-    %td
-      = @runner.version
-  %tr
-    %td
-      Revision
-    %td
-      = @runner.revision
-  %tr
-    %td
-      Platform
-    %td
-      = @runner.platform
-  %tr
-    %td
-      Architecture
-    %td
-      = @runner.architecture
-  %tr
-    %td
-      Description
-    %td
-      = @runner.description
-  %tr
-    %td
-      Last contact
-    %td
-      - if @runner.contacted_at
-        #{time_ago_in_words(@runner.contacted_at)} ago
-      - else
-        Never
+      %td
+        Last contact
+      %td
+        - if @runner.contacted_at
+          #{time_ago_in_words(@runner.contacted_at)} ago
+        - else
+          Never
 
 
-      
+        
diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml
index 1065def693bbbecf3a8846b054a6735f9cda7d9a..c1356f6db02dafca076fc28956a982e22dfdb1e7 100644
--- a/app/views/projects/services/index.html.haml
+++ b/app/views/projects/services/index.html.haml
@@ -2,22 +2,23 @@
 %h3.page-title Project services
 %p.light Project services allow you to integrate GitLab with other applications
 
-%table.table
-  %thead
-    %tr
-      %th
-      %th Service
-      %th Description
-      %th Last edit
-  - @services.sort_by(&:title).each do |service|
-    %tr
-      %td
-        = boolean_to_icon service.activated?
-      %td
-        = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do
-          %strong= service.title
-      %td
-        = service.description
-      %td.light
-        = time_ago_in_words service.updated_at
-        ago
+.table-holder
+  %table.table
+    %thead
+      %tr
+        %th
+        %th Service
+        %th Description
+        %th Last edit
+    - @services.sort_by(&:title).each do |service|
+      %tr
+        %td
+          = boolean_to_icon service.activated?
+        %td
+          = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do
+            %strong= service.title
+        %td
+          = service.description
+        %td.light
+          = time_ago_in_words service.updated_at
+          ago
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index e95d987d74c90f25a3bcb00acde0f7a1c03801ea..585caf674c961982e849ec5fad5dd7bf4e27529b 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -7,8 +7,7 @@
     = render 'shared/no_ssh'
     = render 'shared/no_password'
 
-- if prefer_readme?
-  = render 'projects/last_push'
+= render 'projects/last_push'
 
 = render "home_panel"
 
@@ -28,7 +27,7 @@
       = link_to project_files_path(@project) do
         = repository_size
 
-    - if !prefer_readme? && @repository.readme
+    - if default_project_view != 'readme' && @repository.readme
       %li
         = link_to 'Readme', readme_path(@project)
 
@@ -64,14 +63,12 @@
       = icon("exclamation-triangle fw")
       Archived project! Repository is read-only
 
-%section
-  - if prefer_readme?
-    .project-show-readme
-      = render 'projects/readme'
-  - else
-    .project-show-activity
-      = render 'projects/activity'
+- if @repository.commit
+  .content-block.second-block.white
+    = render 'projects/last_commit', commit: @repository.commit, project: @project
 
+%div{class: "project-show-#{default_project_view}"}
+  = render default_project_view
 
 - if current_user
   - access = user_max_access_in_project(current_user, @project)
diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..4a51546942204346dc8d591ba3bf4c1ade0b7b8b
--- /dev/null
+++ b/app/views/projects/snippets/_actions.html.haml
@@ -0,0 +1,11 @@
+= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do
+  = icon('plus')
+  New Snippet
+- if can?(current_user, :admin_project_snippet, @snippet)
+  = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do
+    = icon('trash-o')
+    Delete
+- if can?(current_user, :update_project_snippet, @snippet)
+  = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
+    = icon('pencil-square-o')
+    Edit
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index 3fed2c9949d4ad9953de9f98c32f485393b49fc9..4af963e14da55f6ad7952dbebfc02f571309b180 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,17 +1,13 @@
 - page_title "Snippets"
 = render "header_title"
 
-%h3.page-title
-  Snippets
-  - if can? current_user, :create_project_snippet, @project
-    = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Snippet" do
-      Add new snippet
+.gray-content-block.top-block
+  .pull-right
+    = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
+      = icon('plus')
+      New Snippet
 
-%p.light
-  Share code pastes with others out of git repository
+  .oneline
+    Share code pastes with others out of git repository
 
-%ul.bordered-list
-  = render partial: "shared/snippets/snippet", collection: @snippets
-  - if @snippets.empty?
-    %li
-      .nothing-here-block Nothing here.
+= render 'snippets/snippets'
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index be7d4d486fa383cdc476bbe42e31de27aeaa7a92..5d706942f2d4cd57080a1067975c39aad0fa9b66 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -1,40 +1,18 @@
 - page_title @snippet.title, "Snippets"
 = render "header_title"
 
-%h3.page-title
-  = @snippet.title
+.snippet-holder
+  = render 'shared/snippets/header'
 
-  .pull-right
-    = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
-      Add new snippet
+  %article.file-holder
+    .file-title
+      = blob_icon 0, @snippet.file_name
+      %strong
+        = @snippet.file_name
+      .file-actions.hidden-xs
+        .btn-group.tree-btn-group
+          = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
 
-%hr
+    = render 'shared/snippets/blob'
 
-.append-bottom-20
-  .pull-right
-    = "##{@snippet.id}"
-    %span.light
-      by
-      = link_to user_path(@snippet.author) do
-        = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16"
-        = @snippet.author_name
-
-  .back-link
-    = link_to namespace_project_snippets_path(@project.namespace, @project) do
-      &larr; project snippets
-
-.file-holder
-  .file-title
-    %i.fa.fa-file
-    %strong
-      = @snippet.file_name
-    .file-actions
-      .btn-group
-        - if can?(current_user, :update_project_snippet, @snippet)
-          = link_to "edit", edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", title: 'Edit Snippet'
-        = link_to "raw", raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
-      - if can?(current_user, :admin_project_snippet, @snippet)
-        = link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet'
-  = render 'shared/snippets/blob'
-
-%div#notes= render "projects/notes/notes_with_form"
+  %div#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..667057ef2d8bfa867b12cbb1835393158186ef7d
--- /dev/null
+++ b/app/views/projects/tags/_download.html.haml
@@ -0,0 +1,17 @@
+%span.btn-group.btn-grouped
+  = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), class: 'btn btn-default', rel: 'nofollow' do
+    %i.fa.fa-download
+    %span source code
+  %a.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown' }
+    %span.caret
+    %span.sr-only
+      Select Archive Format
+  %ul.col-xs-10.dropdown-menu{ role: 'menu' }
+    %li
+      = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do
+        %i.fa.fa-download
+        %span Download zip
+    %li
+      = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
+        %i.fa.fa-download
+        %span Download tar.gz
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 2ca295fc5f3383b51d4d0d3298ec3d50284126e1..e2c5178185e475e03e4d2f8b61e977420b831b77 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -1,22 +1,28 @@
 - commit = @repository.commit(tag.target)
+- release = @releases.find { |release| release.tag == tag.name }
 %li
   %div
-    = link_to namespace_project_commits_path(@project.namespace, @project, tag.name), class: "" do
+    = link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do
       %strong
-        %i.fa.fa-tag
+        = icon('tag')
         = tag.name
     - if tag.message.present?
       &nbsp;
       = strip_gpg_signature(tag.message)
+
     .controls
+      = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do
+        = icon("pencil")
       - if can? current_user, :download_code, @project
-        = render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-xs'
-      - if can?(current_user, :admin_project, @project)
-        = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-xs btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do
-          %i.fa.fa-trash-o
+        = render 'projects/tags/download', ref: tag.name, project: @project
 
   - if commit
     = render 'projects/branches/commit', commit: commit, project: @project
   - else
     %p
       Cant find HEAD commit for this tag
+  - if release && release.description.present?
+    .description.prepend-top-default
+      .wiki
+        = preserve do
+          = markdown release.description
diff --git a/app/views/projects/tags/destroy.js.haml b/app/views/projects/tags/destroy.js.haml
deleted file mode 100644
index ada6710f940638e963947e871f37fd2e22268b41..0000000000000000000000000000000000000000
--- a/app/views/projects/tags/destroy.js.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-$('.js-totaltags-count').html("#{@repository.tags.size}")
-- if @repository.tags.size == 0
-  $('.tags').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000)
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index 9f5c1be125c7e0b04957842b4f1cb3e89cfc5765..e106be794f1a5fd31c0e79c59d69e7afe8f0fa8c 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -5,10 +5,12 @@
   .alert.alert-danger
     %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
     = @error
+
 %h3.page-title
-  %i.fa.fa-code-fork
-  New tag
-= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal" do
+  New git tag
+%hr
+
+= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal tag-form" do
   .form-group
     = label_tag :tag_name, 'Name for new tag', class: 'control-label'
     .col-sm-10
@@ -17,12 +19,29 @@
     = label_tag :ref, 'Create from', class: 'control-label'
     .col-sm-10
       = text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control'
-      .light Branch name or commit SHA
+      .help-block  Branch name or commit SHA
   .form-group
     = label_tag :message, 'Message', class: 'control-label'
     .col-sm-10
       = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control'
-      .light (Optional) Entering a message will create an annotated tag.
+      .help-block (Optional) Entering a message will create an annotated tag.
+  %hr
+  .form-group
+    = label_tag :release_description, 'Release notes', class: 'control-label'
+    .col-sm-10
+      = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
+        .zennable
+          %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox")
+          .zen-backdrop
+            = text_area_tag :release_description, nil, class: 'js-gfm-input markdown-area description js-quick-submit form-control', placeholder: ''
+            %a.zen-enter-link(tabindex="-1" href="#")
+              = icon('expand')
+              Edit in fullscreen
+            %a.zen-leave-link(href="#")
+              = icon('compress')
+
+        = render 'projects/notes/hints'
+        .help-block (Optional) You can add release notes to your tag. It will be stored in the GitLab database and shown on the tags page
   .form-actions
     = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3
     = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel'
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..ebe3718afcc3bbf8cb55f80e5d4ce016c94fd09d
--- /dev/null
+++ b/app/views/projects/tags/show.html.haml
@@ -0,0 +1,39 @@
+- page_title @tag.name, "Tags"
+= render "projects/commits/header_title"
+= render "projects/commits/head"
+
+.gray-content-block
+  .pull-right
+    - if can?(current_user, :push_code, @project)
+      = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn', title: 'Edit release notes' do
+        = icon("pencil")
+    = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse source code' do
+      = icon('files-o')
+    = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse commits' do
+      = icon('history')
+    - if can? current_user, :download_code, @project
+      = render 'projects/tags/download', ref: @tag.name, project: @project
+    - if can?(current_user, :admin_project, @project)
+      .pull-right
+        = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'} do
+          %i.fa.fa-trash-o
+  .title
+    %strong= @tag.name
+    - if @tag.message.present?
+      %span.light
+        &nbsp;
+        = strip_gpg_signature(@tag.message)
+  - if @commit
+    = render 'projects/branches/commit', commit: @commit, project: @project
+  - else
+    Cant find HEAD commit for this tag
+
+
+.append-bottom-default.prepend-top-default
+  - if @release.description.present?
+    .description
+      .wiki
+        = preserve do
+          = markdown @release.description
+  - else
+    This tag has no release notes.
diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml
index 02ecbade219250ceaf038830c7f9da2361a5baba..2ddc5d504fa14f98f06d6b2bbefe1abe13a22003 100644
--- a/app/views/projects/tree/_blob_item.html.haml
+++ b/app/views/projects/tree/_blob_item.html.haml
@@ -4,5 +4,5 @@
     %span.str-truncated
       = link_to blob_item.name, namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name))
   %td.tree_time_ago.cgray
-    = render 'spinner'
+    = render 'projects/tree/spinner'
   %td.hidden-xs.tree_commit
diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index 7e9af19c8ba015e3acf3efd511cca0ee0bce42d2..3c5edf4b033f1a472953fa081343e70486175b1c 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -1,8 +1,8 @@
-%article.file-holder.readme-holder#README
+%article.file-holder.readme-holder
   .file-title
-    = link_to '#README' do
+    = blob_icon readme.mode, readme.name
+    = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
       %strong
-        %i.fa.fa-file
         = readme.name
   .file-content.wiki
     = render_readme(readme)
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree_content.html.haml
similarity index 52%
rename from app/views/projects/tree/_tree.html.haml
rename to app/views/projects/tree/_tree_content.html.haml
index 7ff48e32e601e8a349ad3dc1d1d1e63b9b34c1ed..ee4c9d1693d1c7ded187ef654ea979e090e7fe55 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -1,36 +1,5 @@
-.gray-content-block
-  %ul.breadcrumb.repo-breadcrumb
-    %li
-      = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
-        = @project.path
-    - tree_breadcrumbs(tree, 6) do |title, path|
-      %li
-        - if path
-          = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
-        - else
-          = link_to title, '#'
-    - if allowed_tree_edit?
-      %li
-        %span.dropdown
-          %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"}
-            = icon('plus')
-          %ul.dropdown-menu
-            %li
-              = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
-                = icon('pencil fw')
-                Create file
-            %li
-              = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
-                = icon('file fw')
-                Upload file
-            %li.divider
-            %li
-              = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
-                = icon('folder fw')
-                New directory
-
-%div#tree-content-holder.tree-content-holder
-  .tree-table-holder
+%div.tree-content-holder
+  .table-holder
     %table.table#tree-slider{class: "table_#{@hex_path} tree-table table-striped" }
       %thead
         %tr
@@ -60,8 +29,6 @@
   - if tree.readme
     = render "projects/tree/readme", readme: tree.readme
 
-%div.tree_progress
-
 - if allowed_tree_edit?
   = render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
   = render 'projects/blob/new_dir'
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..1115ca6b4caaf333d997f4eff4efea2243fc218c
--- /dev/null
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -0,0 +1,32 @@
+.tree-ref-holder
+  = render 'shared/ref_switcher', destination: 'tree', path: @path
+
+%ul.breadcrumb.repo-breadcrumb
+  %li
+    = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
+      = @project.path
+  - tree_breadcrumbs(tree, 6) do |title, path|
+    %li
+      - if path
+        = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
+      - else
+        = link_to title, '#'
+  - if allowed_tree_edit?
+    %li
+      %span.dropdown
+        %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"}
+          = icon('plus')
+        %ul.dropdown-menu
+          %li
+            = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
+              = icon('pencil fw')
+              Create file
+          %li
+            = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
+              = icon('file fw')
+              Upload file
+          %li.divider
+          %li
+            = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
+              = icon('folder fw')
+              New directory
diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml
index e87138bf9800bd7d4349d4b450a6dfb3af741353..cf65057e70458e5e9e15b8d1e8d9aacdb1fc20ef 100644
--- a/app/views/projects/tree/_tree_item.html.haml
+++ b/app/views/projects/tree/_tree_item.html.haml
@@ -5,5 +5,5 @@
       - path = flatten_tree(tree_item)
       = link_to path, namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path))
   %td.tree_time_ago.cgray
-    = render 'spinner'
+    = render 'projects/tree/spinner'
   %td.hidden-xs.tree_commit
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index dec4677f83031be6a8d958761139271a1a6cc417..ec14bd7f65a0093caf55e2ca7176a0deb657c9af 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -6,12 +6,12 @@
 
 = render 'projects/last_push'
 
-.tree-ref-holder
-  = render 'shared/ref_switcher', destination: 'tree', path: @path
-
 - if can? current_user, :download_code, @project
   .tree-download-holder
     = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'btn-group pull-right hidden-xs hidden-sm', split_button: true
 
 #tree-holder.tree-holder.clearfix
-  = render "tree", tree: @tree
+  .gray-content-block.top-block
+    = render 'projects/tree/tree_header', tree: @tree
+
+  = render 'projects/tree/tree_content', tree: @tree
diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml
index 17dcb78e256cf6f02940b6170c2b256b0a0df409..b3ad79a200ed43648229f15f0381d779ead3f742 100644
--- a/app/views/projects/triggers/index.html.haml
+++ b/app/views/projects/triggers/index.html.haml
@@ -1,3 +1,4 @@
+- page_title "Triggers"
 %h3.page-title
   Triggers
 
@@ -7,12 +8,13 @@
 %hr.clearfix
 
 -if @triggers.any?
-  %table.table
-    %thead
-      %th Token
-      %th Last used
-      %th
-    = render partial: 'trigger', collection: @triggers, as: :trigger
+  .table-holder
+    %table.table
+      %thead
+        %th Token
+        %th Last used
+        %th
+      = render partial: 'trigger', collection: @triggers, as: :trigger
 - else
   %h4 No triggers
 
diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml
index 29416a94ff6869a692fc63f101c950f33922d7d1..e052da1ac4356ba85aa6bc98e14660b3536bed9c 100644
--- a/app/views/projects/variables/show.html.haml
+++ b/app/views/projects/variables/show.html.haml
@@ -1,3 +1,4 @@
+- page_title "Variables"
 %h3.page-title
   Secret Variables
 
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 261d4a92d7d8006a32a0d80f23194a7e7b32d815..9c94c43e747e776d135344a45dbc1746803ee42b 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -23,9 +23,7 @@
     .col-sm-10
       = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
         = render 'projects/zen', f: f, attr: :content, classes: 'description form-control js-quick-submit'
-        .col-sm-12.hint
-          .pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}
-          .pull-right Attach files by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+        = render 'projects/notes/hints'
 
       .clearfix
       .error-alert
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index bfbef823b35d3d821851565081369feca47a3719..4322146ce340ecbb438f09539ce585d8219353fa 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -7,28 +7,29 @@
     %span.light History for
     = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page)
 
-%table.table
-  %thead
-    %tr
-      %th Page version
-      %th Author
-      %th Commit Message
-      %th Last updated
-      %th Format
-  %tbody
-    - @page.versions.each_with_index do |version, index|
-      - commit = version
+.table-holder
+  %table.table
+    %thead
       %tr
-        %td
-          = link_to project_wiki_path_with_version(@project, @page,
-                                                   commit.id, index == 0) do
-            = truncate_sha(commit.id)
-        %td
-          = commit.author.name
-        %td
-          = commit.message
-        %td
-          #{time_ago_with_tooltip(version.authored_date)}
-        %td
-          %strong
-            = @page.page.wiki.page(@page.page.name, commit.id).try(:format)
+        %th Page version
+        %th Author
+        %th Commit Message
+        %th Last updated
+        %th Format
+    %tbody
+      - @page.versions.each_with_index do |version, index|
+        - commit = version
+        %tr
+          %td
+            = link_to project_wiki_path_with_version(@project, @page,
+                                                     commit.id, index == 0) do
+              = truncate_sha(commit.id)
+          %td
+            = commit.author.name
+          %td
+            = commit.message
+          %td
+            #{time_ago_with_tooltip(version.authored_date)}
+          %td
+            %strong
+              = @page.page.wiki.page(@page.page.name, commit.id).try(:format)
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
index d637abfa76b90588840b1e98c209e0c7521b7fd4..481451edb23ce0904bbb70b6e15fd2612faae374 100644
--- a/app/views/search/_category.html.haml
+++ b/app/views/search/_category.html.haml
@@ -42,6 +42,13 @@
           Wiki
           %span.badge
             = @search_results.wiki_blobs_count
+    %li{class: ("active" if @scope == 'commits')}
+      = link_to search_filter_path(scope: 'commits') do
+        = icon('history fw')
+        %span
+          Commits
+          %span.badge
+            = @search_results.commits_count
 
   - elsif @show_snippets
     %li{class: ("active" if @scope == 'snippet_blobs')}
diff --git a/app/views/search/results/_commit.html.haml b/app/views/search/results/_commit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..4e6c3965dc65dac0a96f427c8466b2091df7b37e
--- /dev/null
+++ b/app/views/search/results/_commit.html.haml
@@ -0,0 +1,2 @@
+.search-result-row
+  = render 'projects/commits/commit', project: @project, commit: commit
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index b23b2f0d5eb26f89b2f5dbf10cab59d7b5c57d4d..2e4aab36301833c1fa63becd42024df25f5dd7f1 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -6,7 +6,7 @@
         type: 'button', |
         class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", |
         :"data-clone" => project.ssh_url_to_repo, |
-        :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH",
+        :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH.",
         :"data-html" => "true",
         :"data-container" => "body"}
         SSH
@@ -15,7 +15,7 @@
         type: 'button', |
         class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", |
         :"data-clone" => project.http_url_to_repo, |
-        :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}",
+        :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}.",
         :"data-html" => "true",
         :"data-container" => "body"}
         = gitlab_config.protocol.upcase
diff --git a/app/views/shared/_logo.svg b/app/views/shared/_logo.svg
new file mode 100644
index 0000000000000000000000000000000000000000..da49c48acd3f54b15148477140c9d82e9b79fd33
--- /dev/null
+++ b/app/views/shared/_logo.svg
@@ -0,0 +1,21 @@
+<svg width="36px" height="36px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="tanuki-logo">
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)">
+            <g id="Page-1" sketch:type="MSShapeGroup">
+                <g id="Fill-1-+-Group-24">
+                    <g id="Group-24">
+                        <g id="Group">
+                            <path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329" class="tanuki-shape"></path>
+                            <path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26" class="tanuki-shape"></path>
+                            <path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326" class="tanuki-shape"></path>
+                            <path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329" class="tanuki-shape"></path>
+                            <path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26" class="tanuki-shape"></path>
+                            <path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326" class="tanuki-shape"></path>
+                            <path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329" class="tanuki-shape"></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>
diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml
index cba18c14568710fddff4b99341cbdd1a07b7991a..be66256c7b052d3ed05273afab50fa6faadd7922 100644
--- a/app/views/shared/issuable/_context.html.haml
+++ b/app/views/shared/issuable/_context.html.haml
@@ -45,6 +45,6 @@
         .description-block.subscribed{class: ( 'hidden' unless subscribed )}
           You're receiving notifications because you're subscribed to this thread.
 
-:coffeescript
-  new Subscription("#{toggle_subscription_path(issuable)}")
-  new IssuableContext()
+:javascript
+  new Subscription("#{toggle_subscription_path(issuable)}");
+  new IssuableContext();
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index 8f16773077e9d658c86338927b3b5adff1ed7920..d1231438ee4df03c2f04743dbce48957cdb737be 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -42,11 +42,10 @@
             class: 'select2 trigger-submit', include_blank: true,
             data: {placeholder: 'Milestone'})
 
-        - if @project
-          .filter-item.inline.labels-filter
-            = select_tag('label_name', project_labels_options(@project),
-              class: 'select2 trigger-submit', include_blank: true,
-              data: {placeholder: 'Label'})
+        .filter-item.inline.labels-filter
+          = select_tag('label_name', projects_labels_options,
+            class: 'select2 trigger-submit', include_blank: true,
+            data: {placeholder: 'Label'})
 
         .pull-right
           = render 'shared/sort_dropdown'
@@ -61,9 +60,9 @@
           = hidden_field_tag :state_event, params[:state_event]
           = button_tag "Update issues", class: "btn update_selected_issues btn-save"
 
-:coffeescript
-  new UsersSelect()
-
-  $('form.filter-form').on 'submit', (event) ->
-    event.preventDefault()
-    Turbolinks.visit @.action + '&' + $(@).serialize()
+:javascript
+  new UsersSelect();
+  $('form.filter-form').on('submit', function (event) {
+    event.preventDefault();
+    Turbolinks.visit(this.action + '&' + $(this).serialize());
+  });
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 594e54f404c155ac0767780e4968dece3a9c4900..0fc74d7d2b162825a1e086d54da83811bca0390e 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -27,14 +27,7 @@
     = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
       = render 'projects/zen', f: f, attr: :description,
                                classes: 'description form-control js-quick-submit'
-      .col-sm-12.hint
-        .pull-left
-          Parsed with
-          #{link_to 'GitLab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}.
-        .pull-right
-          Attach files by dragging &amp; dropping
-          or #{link_to 'selecting them', '#', class: 'markdown-selector' }.
-
+      = render 'projects/notes/hints'
       .clearfix
       .error-alert
   %hr
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 357cfd6a37007542ca262155848ece0f66d85b06..e5ffe1e29ae688ce52c83a04ce200e8a5a22b367 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -17,5 +17,5 @@
         = link_to '#', class: 'js-expand' do
           Show all
 
-:coffeescript
-  new ProjectsList()
+:javascript
+  new ProjectsList();
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index aee839b44e7ee43d15749ec3215142c606f56a4b..c36995b94d7dd20e1d9f70cedda4d0663f1150e5 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -21,9 +21,7 @@
     .project-controls
       - if ci && !project.empty_repo? && project.commit
         - if ci_commit = project.ci_commit(project.commit.sha)
-          = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}",
-            title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do
-            = ci_status_icon(ci_commit)
+          = render_ci_status(ci_commit)
           &nbsp;
       - if stars
         %span
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..0a4a790ec5ee182b5ad0e5a62178dd0636246e45
--- /dev/null
+++ b/app/views/shared/snippets/_header.html.haml
@@ -0,0 +1,24 @@
+.snippet-details
+  .page-title
+    .snippet-box{class: visibility_level_color(@snippet.visibility_level)}
+      = visibility_level_icon(@snippet.visibility_level)
+      = visibility_level_label(@snippet.visibility_level)
+    %span.snippet-id Snippet ##{@snippet.id}
+    %span.creator
+      &middot; created by #{link_to_member(@project, @snippet.author, size: 24)}
+      &middot;
+      = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago')
+      - if @snippet.updated_at != @snippet.created_at
+        %span
+          &middot;
+          = icon('edit', title: 'edited')
+          = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago')
+
+    .pull-right
+      - if @snippet.project_id?
+        = render "projects/snippets/actions"
+      - else
+        = render "snippets/actions"
+  .gray-content-block.middle-block
+    %h2.snippet-title
+      = gfm escape_once(@snippet.title)
diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml
index 69a713ad9aacf4b20cae64fbfaca0bbc10e09779..c6294caddc7f7d26d8d9fc2d002f5a3d3e9b38bd 100644
--- a/app/views/shared/snippets/_snippet.html.haml
+++ b/app/views/shared/snippets/_snippet.html.haml
@@ -18,4 +18,3 @@
       = image_tag avatar_icon(snippet.author_email), class: "avatar s24", alt: ''
       = snippet.author_name
     authored #{time_ago_with_tooltip(snippet.created_at)}
-
diff --git a/app/views/sherlock/file_samples/show.html.haml b/app/views/sherlock/file_samples/show.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..cfd11e45b6af80d5721f73379ea6074498d7136e
--- /dev/null
+++ b/app/views/sherlock/file_samples/show.html.haml
@@ -0,0 +1,55 @@
+- page_title t('sherlock.title'), t('sherlock.transaction'),
+  t('sherlock.file_sample')
+
+- header_title t('sherlock.title'), sherlock_transactions_path
+
+.gray-content-block
+  .pull-right
+    = link_to(sherlock_transaction_path(@transaction), class: 'btn') do
+      %i.fa.fa-arrow-left
+      = t('sherlock.transaction')
+  .oneline
+    = t('sherlock.file_sample')
+    = @file_sample.id
+
+.prepend-top-default
+  %p
+    %span.light
+      #{t('sherlock.time')}:
+    %strong
+      = @file_sample.duration.round(2)
+      = t('sherlock.milliseconds')
+  %p
+    %span.light
+      #{t('sherlock.events')}:
+    %strong
+      = @file_sample.events
+
+%article.file-holder
+  .file-title
+    %i.fa.fa-file-text-o.fa-fw
+    %strong
+      = @file_sample.file
+  .code.file-content.js-syntax-highlight
+    .line-numbers
+      %table.sherlock-line-samples-table
+        %thead
+          %tr
+            %th= t('sherlock.line_capitalized')
+            %th= t('sherlock.events')
+            %th= t('sherlock.time')
+            %th= t('sherlock.percent')
+        %tbody
+          - @file_sample.line_samples.each_with_index do |sample, index|
+            %tr{class: sample.majority_of?(@file_sample.duration) ? 'slow' : ''}
+              %td= index + 1
+              %td= sample.events
+              %td
+                = sample.duration.round(2)
+                = t('sherlock.milliseconds')
+              %td
+                = sample.percentage_of(@file_sample.duration).round
+                = t('sherlock.percent')
+
+    .sherlock-file-sample
+      = highlight(@file_sample.file, @file_sample.source)
diff --git a/app/views/sherlock/queries/_backtrace.html.haml b/app/views/sherlock/queries/_backtrace.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..5c9294c0ab534e0f2879707e06fa119483aab0e8
--- /dev/null
+++ b/app/views/sherlock/queries/_backtrace.html.haml
@@ -0,0 +1,27 @@
+.prepend-top-default
+  .panel.panel-default
+    .panel-heading
+      %strong
+        = t('sherlock.application_backtrace')
+    %ul.well-list
+      - @query.application_backtrace.each do |location|
+        %li
+          = location.path
+          %small.light
+            = t('sherlock.line')
+            = location.line
+
+  .panel.panel-default
+    .panel-heading
+      %strong
+        = t('sherlock.full_backtrace')
+    %ul.well-list
+      - @query.backtrace.each do |location|
+        %li
+          - if location.application?
+            %strong= location.path
+          - else
+            = location.path
+          %small.light
+            = t('sherlock.line')
+            = location.line
diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..549b47430e697f761c340f5738da302bda36839e
--- /dev/null
+++ b/app/views/sherlock/queries/_general.html.haml
@@ -0,0 +1,50 @@
+.prepend-top-default
+  .panel.panel-default
+    .panel-heading
+      %strong
+        = t('sherlock.general')
+    %ul.well-list
+      %li
+        %span.light
+          #{t('sherlock.time')}:
+        %strong
+          = @query.duration.round(4)
+          = t('sherlock.milliseconds')
+      %li
+        %span.light
+          #{t('sherlock.origin')}:
+        %strong
+          = @query.last_application_frame.path
+        %small.light
+          = t('sherlock.line')
+          = @query.last_application_frame.line
+
+  .panel.panel-default
+    .panel-heading
+      .pull-right
+        %button.js-clipboard-trigger.btn.btn-xs{title: t('sherlock.copy_to_clipboard'), type: :button}
+          %i.fa.fa-clipboard
+        %pre.hidden
+          = @query.formatted_query
+      %strong
+        = t('sherlock.query')
+    %ul.well-list
+      %li
+        .code.js-syntax-highlight.sherlock-code
+          :preserve
+            #{highlight("#{@query.id}.sql", @query.formatted_query)}
+
+  .panel.panel-default
+    .panel-heading
+      .pull-right
+        %button.js-clipboard-trigger.btn.btn-xs{title: t('sherlock.copy_to_clipboard'), type: :button}
+          %i.fa.fa-clipboard
+        %pre.hidden
+          = @query.explain
+      %strong
+        = t('sherlock.query_plan')
+    %ul.well-list
+      %li
+        .code.js-syntax-highlight.sherlock-code
+          %pre
+            %code= @query.explain
diff --git a/app/views/sherlock/queries/show.html.haml b/app/views/sherlock/queries/show.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..4a84348ac82582f753d40c81826bdb17684bb4b5
--- /dev/null
+++ b/app/views/sherlock/queries/show.html.haml
@@ -0,0 +1,26 @@
+- page_title t('sherlock.title'), t('sherlock.transaction'), t('sherlock.query')
+- header_title t('sherlock.title'), sherlock_transactions_path
+
+%ul.center-top-menu
+  %li.active
+    %a(href="#tab-general" data-toggle="tab")
+      = t('sherlock.general')
+  %li
+    %a(href="#tab-backtrace" data-toggle="tab")
+      = t('sherlock.backtrace')
+
+.gray-content-block
+  .pull-right
+    = link_to(sherlock_transaction_path(@transaction), class: 'btn') do
+      %i.fa.fa-arrow-left
+      = t('sherlock.transaction')
+  .oneline
+    = t('sherlock.query')
+    = @query.id
+
+.tab-content
+  .tab-pane.active#tab-general
+    = render(partial: 'general')
+
+  .tab-pane#tab-backtrace
+    = render(partial: 'backtrace')
diff --git a/app/views/sherlock/transactions/_file_samples.html.haml b/app/views/sherlock/transactions/_file_samples.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..4349c9b7aceb206e563e056546f1656125cf79cf
--- /dev/null
+++ b/app/views/sherlock/transactions/_file_samples.html.haml
@@ -0,0 +1,24 @@
+- if @transaction.file_samples.empty?
+  .nothing-here-block
+    = t('sherlock.no_file_samples')
+- else
+  .table-holder
+    %table.table
+      %thead
+        %tr
+          %th= t('sherlock.time_inclusive')
+          %th= t('sherlock.count')
+          %th= t('sherlock.path')
+          %th
+      %tbody
+        - @transaction.sorted_file_samples.each do |sample|
+          %tr
+            %td
+              = sample.duration.round(2)
+              = t('sherlock.milliseconds')
+            %td= @transaction.view_counts.fetch(sample.file, 1)
+            %td= sample.relative_path
+            %td
+              = link_to(t('sherlock.view'),
+                sherlock_transaction_file_sample_path(@transaction, sample),
+                class: 'btn btn-xs')
diff --git a/app/views/sherlock/transactions/_general.html.haml b/app/views/sherlock/transactions/_general.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..4287a0c32036f49cccd1b7ac1482186d7f1f5254
--- /dev/null
+++ b/app/views/sherlock/transactions/_general.html.haml
@@ -0,0 +1,33 @@
+.prepend-top-default
+  .panel.panel-default
+    .panel-heading
+      %strong
+        = t('sherlock.general')
+    %ul.well-list
+      %li
+        %span.light
+          #{t('sherlock.id')}:
+        %strong
+          = @transaction.id
+      %li
+        %span.light
+          #{t('sherlock.type')}:
+        %strong
+          = @transaction.type
+      %li
+        %span.light
+          #{t('sherlock.path')}:
+        %strong
+          = @transaction.path
+      %li
+        %span.light
+          #{t('sherlock.time')}:
+        %strong
+          = @transaction.duration.round(2)
+          = t('sherlock.seconds')
+      %li
+        %span.light
+          #{t('sherlock.finished_at')}:
+        %strong
+          = time_ago_in_words(@transaction.finished_at)
+          = t('sherlock.ago')
diff --git a/app/views/sherlock/transactions/_queries.html.haml b/app/views/sherlock/transactions/_queries.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..b7e0162e80db835d22bc1b3aa44933ee6c3141ad
--- /dev/null
+++ b/app/views/sherlock/transactions/_queries.html.haml
@@ -0,0 +1,24 @@
+- if @transaction.queries.empty?
+  .nothing-here-block
+    = t('sherlock.no_queries')
+- else
+  .table-holder
+    %table.table#sherlock-queries
+      %thead
+        %tr
+          %th= t('sherlock.time')
+          %th= t('sherlock.query')
+          %td
+      %tbody
+        - @transaction.sorted_queries.each do |query|
+          %tr
+            %td
+              = query.duration.round(2)
+              = t('sherlock.milliseconds')
+            %td
+              .code.js-syntax-highlight.sherlock-code
+                = highlight("#{query.id}.sql", query.formatted_query)
+            %td
+              = link_to(t('sherlock.view'),
+                sherlock_transaction_query_path(@transaction, query),
+                class: 'btn btn-xs')
diff --git a/app/views/sherlock/transactions/index.html.haml b/app/views/sherlock/transactions/index.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..010e1a2a9023e8d5a5d96fc63f67c0963cfebd4a
--- /dev/null
+++ b/app/views/sherlock/transactions/index.html.haml
@@ -0,0 +1,42 @@
+- page_title t('sherlock.title')
+- header_title t('sherlock.title'), sherlock_transactions_path
+
+.gray-content-block
+  .pull-right
+    = link_to(destroy_all_sherlock_transactions_path,
+      class: 'btn btn-danger',
+      method: :delete) do
+      %i.fa.fa-trash
+      = t('sherlock.delete_all_transactions')
+  .oneline= t('sherlock.introduction')
+
+- if @transactions.empty?
+  .nothing-here-block= t('sherlock.no_transactions')
+- else
+  .table-holder
+    %table.table
+      %thead
+        %tr
+          %th= t('sherlock.type')
+          %th= t('sherlock.path')
+          %th= t('sherlock.time')
+          %th= t('sherlock.queries')
+          %th= t('sherlock.finished_at')
+          %th
+      %tbody
+        - @transactions.each do |trans|
+          %tr
+            %td= trans.type
+            %td
+              %span{title: trans.path}
+                = truncate(trans.path, length: 70)
+            %td
+              = trans.duration.round(2)
+              = t('sherlock.seconds')
+            %td= trans.queries.length
+            %td
+              = time_ago_in_words(trans.finished_at)
+              = t('sherlock.ago')
+            %td
+              = link_to(sherlock_transaction_path(trans), class: 'btn btn-xs') do
+                = t('sherlock.view')
diff --git a/app/views/sherlock/transactions/show.html.haml b/app/views/sherlock/transactions/show.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..3c8ffb066484125b3cb1b2880fb7e3f8e305530c
--- /dev/null
+++ b/app/views/sherlock/transactions/show.html.haml
@@ -0,0 +1,36 @@
+- page_title t('sherlock.title'), t('sherlock.transaction')
+- header_title t('sherlock.title'), sherlock_transactions_path
+
+%ul.center-top-menu
+  %li.active
+    %a(href="#tab-general" data-toggle="tab")
+      = t('sherlock.general')
+  %li
+    %a(href="#tab-queries" data-toggle="tab")
+      = t('sherlock.queries')
+      %span.badge
+        #{@transaction.queries.length}
+  %li
+    %a(href="#tab-file-samples" data-toggle="tab")
+      = t('sherlock.file_samples')
+      %span.badge
+        #{@transaction.file_samples.length}
+
+.gray-content-block
+  .pull-right
+    = link_to(sherlock_transactions_path, class: 'btn') do
+      %i.fa.fa-arrow-left
+      = t('sherlock.all_transactions')
+  .oneline
+    = t('sherlock.transaction')
+    = @transaction.id
+
+.tab-content
+  .tab-pane.active#tab-general
+    = render(partial: 'general')
+
+  .tab-pane#tab-queries
+    = render(partial: 'queries')
+
+  .tab-pane#tab-file-samples
+    = render(partial: 'file_samples')
diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..1979ae6d5bc473ebf218d9633bee889a747408cc
--- /dev/null
+++ b/app/views/snippets/_actions.html.haml
@@ -0,0 +1,11 @@
+= link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do
+  = icon('plus')
+  New Snippet
+- if can?(current_user, :update_personal_snippet, @snippet)
+  = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do
+    = icon('pencil-square-o')
+    Edit
+- if can?(current_user, :admin_personal_snippet, @snippet)
+  = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do
+    = icon('trash-o')
+    Delete
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 97374e073dc2447b1f06c7bdf850b33f896f36f2..69d8899d4c1d1467b2cb1e8f44261f62de2449f4 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -1,41 +1,14 @@
 - page_title @snippet.title, "Snippets"
-%h4.page-title
-  = @snippet.title
 
-  - if @snippet.private?
-    %span.label.label-success
-      %i.fa.fa-lock
-      private
-
-  .pull-right
-    = link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do
-      Add new snippet
-
-.append-bottom-10.prepend-top-10
-  .pull-right
-    %span.light
-      created by
-      = link_to user_snippets_path(@snippet.author) do
-        = @snippet.author_name
-
-  .back-link
-    - if @snippet.author == current_user
-      = link_to dashboard_snippets_path do
-        &larr; your snippets
-    - else
-      = link_to explore_snippets_path do
-        &larr; explore snippets
-
-.file-holder
-  .file-title
-    %i.fa.fa-file
-    %strong
-      = @snippet.file_name
-    .file-actions
-      .btn-group
-        - if can?(current_user, :update_personal_snippet, @snippet)
-          = link_to "edit", edit_snippet_path(@snippet), class: "btn btn-sm", title: 'Edit Snippet'
-        = link_to "raw", raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
-      - if can?(current_user, :admin_personal_snippet, @snippet)
-        = link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet'
-  = render 'shared/snippets/blob'
+.snippet-holder
+  = render 'shared/snippets/header'
+
+  %article.file-holder
+    .file-title
+      = blob_icon 0, @snippet.file_name
+      %strong
+        = @snippet.file_name
+      .file-actions.hidden-xs
+        .btn-group.tree-btn-group
+          = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
+    = render 'shared/snippets/blob'
diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml
deleted file mode 100644
index a126a858ea85f0ce96e785dc2ebc0297a161d38f..0000000000000000000000000000000000000000
--- a/app/views/users/_projects.html.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present?
-  .panel.panel-default.contributed-projects
-    .panel-heading Projects contributed to
-    = render 'shared/projects/list',
-      projects: contributed_projects.sort_by(&:star_count).reverse,
-      projects_limit: 5, stars: true, avatar: false
-
-- if local_assigns.has_key?(:projects) && projects.present?
-  .panel.panel-default
-    .panel-heading Personal projects
-    = render 'shared/projects/list',
-      projects: projects.sort_by(&:star_count).reverse,
-      projects_limit: 10, stars: true, avatar: false
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
index 922b0c6cebf1cb1425ec056195bc15025cf434d2..7f29918dba371da38fb3950388f9fed69d0d7ae2 100644
--- a/app/views/users/calendar.html.haml
+++ b/app/views/users/calendar.html.haml
@@ -1,7 +1,3 @@
-%h4
-  Contributions calendar
-  .pull-right
-    %small Issues, merge requests and push events
 #cal-heatmap.calendar
   :javascript
     new Calendar(
@@ -10,3 +6,5 @@
       #{@starting_month},
       '#{user_calendar_activities_path}'
     );
+
+.calendar-hint Summary of issues, merge requests and push events
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 11beb3e3239533ecbd60ca6907a712ca3c78af3d..30992412184e6a4cea4b00d86122c3912fd5cfd2 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -6,61 +6,114 @@
 
 = render 'shared/show_aside'
 
-.row
-  %section.col-md-7
-    .header-with-avatar
-      = link_to avatar_icon(@user.email, 400), target: '_blank' do
-        = image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: ''
-      %h3
-        = @user.name
-        - if @user == current_user
-          .pull-right.hidden-xs
-            = link_to profile_path, class: 'btn btn-sm' do
-              = icon('user')
-              Profile settings
-        - elsif current_user
-          .report_abuse.pull-right
-            - if @user.abuse_report
-              %span#report_abuse_btn.light.btn.btn-sm.btn-close{title: 'Already reported for abuse', data: {toggle: 'tooltip', placement: 'right', container: 'body'}}
-                = icon('exclamation-circle')
-            - else
-              %a.light.btn.btn-sm{href: new_abuse_report_path(user_id: @user.id), title: 'Report abuse', data: {toggle: 'tooltip', placement: 'right', container: 'body'}}
-                = icon('exclamation-circle')
+.cover-block
+  .avatar-holder
+    = link_to avatar_icon(@user, 400), target: '_blank' do
+      = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: ''
+  .cover-title
+    = @user.name
 
-      .username
-        @#{@user.username}
-      .description
-        - if @user.bio.present?
-          = @user.bio
+  .cover-desc
+    %span
+      @#{@user.username}.
+    - if @user.bio.present?
+      %span
+        #{@user.bio}.
+    %span
+      Member since #{@user.created_at.stamp("Aug 21, 2011")}
 
-    .clearfix
+  .cover-desc
+    - unless @user.public_email.blank?
+      .profile-link-holder
+        = link_to @user.public_email, "mailto:#{@user.public_email}"
+    - unless @user.skype.blank?
+      .profile-link-holder
+        = link_to "skype:#{@user.skype}", title: "Skype" do
+          = icon('skype')
+    - unless @user.linkedin.blank?
+      .profile-link-holder
+        = link_to "http://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do
+          = icon('linkedin-square')
+    - unless @user.twitter.blank?
+      .profile-link-holder
+        = link_to "http://www.twitter.com/#{@user.twitter}", title: "Twitter" do
+          = icon('twitter-square')
+    - unless @user.website_url.blank?
+      .profile-link-holder
+        = link_to @user.short_website_url, @user.full_website_url
+    - unless @user.location.blank?
+      .profile-link-holder
+        = icon('map-marker')
+        = @user.location
 
-    - if @groups.any?
-      .prepend-top-20
-        %h4 Groups
-        = render 'groups', groups: @groups
-        %hr
 
-    .hidden-xs
-      .user-calendar
-        %h4.center.light
-          %i.fa.fa-spinner.fa-spin
-      .user-calendar-activities
-      %hr
-    %h4
-      User Activity
+  .cover-controls
+    - if @user == current_user
+      = link_to profile_path, class: 'btn btn-gray' do
+        = icon('pencil')
+    - elsif current_user
+      %span.report-abuse
+        - if @user.abuse_report
+          %button.btn.btn-danger{ title: 'Already reported for abuse',
+            data: { toggle: 'tooltip', placement: 'left', container: 'body' }}
+            = icon('exclamation-circle')
+        - else
+          = link_to new_abuse_report_path(user_id: @user.id), class: 'btn btn-gray',
+            title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do
+            = icon('exclamation-circle')
+    - if current_user
+      &nbsp;
+      = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
+        = icon('rss')
 
-      - if current_user
-        %span.rss-icon.pull-right
-          = link_to user_path(@user, :atom, { private_token: current_user.private_token }) do
-            %strong
-              %i.fa.fa-rss
+.gray-content-block.second-block
+  .user-calendar
+    %h4.center.light
+      %i.fa.fa-spinner.fa-spin
+  .user-calendar-activities
 
+
+%ul.center-middle-menu
+  %li.active
+    = link_to "#activity", 'data-toggle' => 'tab' do
+      Activity
+  - if @groups.any?
+    %li
+      = link_to "#groups", 'data-toggle' => 'tab' do
+        Groups
+  - if @contributed_projects.present?
+    %li
+      = link_to "#contributed", 'data-toggle' => 'tab' do
+        Contributed projects
+  - if @projects.present?
+    %li
+      = link_to "#personal", 'data-toggle' => 'tab' do
+        Personal projects
+
+.tab-content
+  .tab-pane.active#activity
     .content_list
     = spinner
-  %aside.col-md-5
-    = render 'profile', user: @user
-    = render 'projects', projects: @projects, contributed_projects: @contributed_projects
 
-:coffeescript
-  $(".user-calendar").load("#{user_calendar_path}")
+  - if @groups.any?
+    .tab-pane#groups
+      %ul.content-list
+        - @groups.each do |group|
+          = render 'shared/groups/group', group: group
+
+  - if @contributed_projects.present?
+    .tab-pane#contributed
+      .contributed-projects
+        = render 'shared/projects/list',
+          projects: @contributed_projects.sort_by(&:star_count).reverse,
+          projects_limit: 5, stars: true, avatar: true
+
+  - if @projects.present?
+    .tab-pane#personal
+      .personal-projects
+        = render 'shared/projects/list',
+          projects: @projects.sort_by(&:star_count).reverse,
+          projects_limit: 10, stars: true, avatar: true
+
+:javascript
+  $(".user-calendar").load("#{user_calendar_path}");
diff --git a/app/workers/repository_archive_cache_worker.rb b/app/workers/repository_archive_cache_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..47c5a670ed4d17eb00a71ef7c4ccd542577c859e
--- /dev/null
+++ b/app/workers/repository_archive_cache_worker.rb
@@ -0,0 +1,9 @@
+class RepositoryArchiveCacheWorker
+  include Sidekiq::Worker
+
+  sidekiq_options queue: :default
+
+  def perform
+    Repository.clean_old_archives
+  end
+end
diff --git a/app/workers/repository_archive_worker.rb b/app/workers/repository_archive_worker.rb
deleted file mode 100644
index 021c1139568b8bfc5ca6b259d57bb16bbaab5d7f..0000000000000000000000000000000000000000
--- a/app/workers/repository_archive_worker.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-class RepositoryArchiveWorker
-  include Sidekiq::Worker
-
-  sidekiq_options queue: :archive_repo
-
-  attr_accessor :project, :ref, :format
-
-  def perform(project_id, ref, format)
-    @project = Project.find(project_id)
-    @ref, @format = ref, format.downcase
-
-    repository = project.repository
-
-    repository.clean_old_archives
-
-    return unless file_path
-    return if archived? || archiving?
-
-    repository.archive_repo(ref, storage_path, format)
-  end
-
-  private
-
-  def storage_path
-    Gitlab.config.gitlab.repository_downloads_path
-  end
-
-  def file_path
-    @file_path ||= project.repository.archive_file_path(ref, storage_path, format)
-  end
-
-  def pid_file_path
-    @pid_file_path ||= project.repository.archive_pid_file_path(ref, storage_path, format)
-  end
-
-  def archived?
-    File.exist?(file_path)
-  end
-
-  def archiving?
-    File.exist?(pid_file_path)
-  end
-end
diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ad02a3b16d953b46eb5901e2be171a2211aa4382
--- /dev/null
+++ b/app/workers/stuck_ci_builds_worker.rb
@@ -0,0 +1,18 @@
+class StuckCiBuildsWorker
+  include Sidekiq::Worker
+  include Sidetiq::Schedulable
+
+  BUILD_STUCK_TIMEOUT = 1.day
+
+  recurrence { daily }
+
+  def perform
+    Rails.logger.info 'Cleaning stuck builds'
+
+    builds = Ci::Build.running_or_pending.where('updated_at < ?', BUILD_STUCK_TIMEOUT.ago)
+    builds.find_each(batch_size: 50).each do |build|
+      Rails.logger.debug "Dropping stuck #{build.status} build #{build.id} for runner #{build.runner_id}"
+      build.drop
+    end
+  end
+end
diff --git a/config/environments/development.rb b/config/environments/development.rb
index d7d6aed1602ca498a7666504b932bbe420c9eedd..827a110c2492ac5349277065d2a992243760bc75 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -24,7 +24,7 @@ Gitlab::Application.configure do
 
   # Expands the lines which load the assets
   # config.assets.debug = true
-  
+
   # Adds additional error checking when serving assets at runtime.
   # Checks for improperly declared sprockets dependencies.
   # Raises helpful error messages.
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 4f7f0b6ef190633eda7d78da2a10925bb0fc4e93..20894ebcdc985ad68b402832a7fd721a5469ae2d 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -99,7 +99,29 @@ production: &base
   # For documentation on how to set this up, see http://doc.gitlab.com/ce/incoming_email/README.html
   incoming_email:
     enabled: false
-    address: "incoming+%{key}@gitlab.example.com"
+
+    # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+    # The `%{key}` placeholder is added after the user part, after a `+` character, before the `@`.
+    address: "gitlab-incoming+%{key}@gmail.com"
+
+    # Email account username
+    # With third party providers, this is usually the full email address.
+    # With self-hosted email servers, this is usually the user part of the email address.
+    user: "gitlab-incoming@gmail.com"
+    # Email account password
+    password: "[REDACTED]"
+
+    # IMAP server host
+    host: "imap.gmail.com"
+    # IMAP server port
+    port: 993
+    # Whether the IMAP server uses SSL
+    ssl: true
+    # Whether the IMAP server uses StartTLS
+    start_tls: false
+
+    # The mailbox where incoming mail will end up. Usually "inbox".
+    mailbox: "inbox"
 
   ## Gravatar
   ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
@@ -252,27 +274,28 @@ production: &base
     # arguments, followed by optional 'args' which can be either a hash or an array.
     # Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html
     providers:
-      # - { name: 'google_oauth2',
-      #     label: 'Google',
-      #     app_id: 'YOUR_APP_ID',
-      #     app_secret: 'YOUR_APP_SECRET',
-      #     args: { access_type: 'offline', approval_prompt: '' } }
-      # - { name: 'twitter',
-      #     app_id: 'YOUR_APP_ID',
-      #     app_secret: 'YOUR_APP_SECRET' }
       # - { name: 'github',
-      #     label: 'GitHub',
       #     app_id: 'YOUR_APP_ID',
       #     app_secret: 'YOUR_APP_SECRET',
       #     args: { scope: 'user:email' } }
+      # - { name: 'bitbucket',
+      #     app_id: 'YOUR_APP_ID',
+      #     app_secret: 'YOUR_APP_SECRET' }
       # - { name: 'gitlab',
-      #     label: 'GitLab.com',
       #     app_id: 'YOUR_APP_ID',
       #     app_secret: 'YOUR_APP_SECRET',
       #     args: { scope: 'api' } }
-      # - { name: 'bitbucket',
+      # - { name: 'google_oauth2',
+      #     app_id: 'YOUR_APP_ID',
+      #     app_secret: 'YOUR_APP_SECRET',
+      #     args: { access_type: 'offline', approval_prompt: '' } }
+      # - { name: 'facebook',
       #     app_id: 'YOUR_APP_ID',
       #     app_secret: 'YOUR_APP_SECRET' }
+      # - { name: 'twitter',
+      #     app_id: 'YOUR_APP_ID',
+      #     app_secret: 'YOUR_APP_SECRET' }
+      #
       # - { name: 'saml',
       #     label: 'Our SAML Provider',
       #     args: {
@@ -288,6 +311,10 @@ production: &base
       #       application_name: 'YOUR_APP_NAME',
       #       application_password: 'YOUR_APP_PASSWORD' } }
 
+  # Shared file storage settings
+  shared:
+    # path: /mnt/gitlab # Default: shared
+
 
 
 
@@ -296,10 +323,12 @@ production: &base
   # ==========================
 
   # GitLab Satellites
+  #
+  # Note for maintainers: keep the satellites.path setting until GitLab 9.0 at
+  # least. This setting is fed to 'rm -rf' in
+  # db/migrate/20151023144219_remove_satellites.rb
   satellites:
-    # Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
     path: /home/git/gitlab-satellites/
-    timeout: 30
 
   ## Backup settings
   backup:
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 4c78bd6e2facf2471ac88a66b60baf5c7c735f69..8192d727f2ace7200e81b688761bd2b579b5450e 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -125,6 +125,9 @@ Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link
 
 Settings.omniauth['providers']  ||= []
 
+Settings['shared'] ||= Settingslogic.new({})
+Settings.shared['path'] = File.expand_path(Settings.shared['path'] || "shared", Rails.root)
+
 Settings['issues_tracker']  ||= {}
 
 #
@@ -169,7 +172,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g
 Settings.gitlab.default_projects_features['wiki']           = true if Settings.gitlab.default_projects_features['wiki'].nil?
 Settings.gitlab.default_projects_features['snippets']       = false if Settings.gitlab.default_projects_features['snippets'].nil?
 Settings.gitlab.default_projects_features['visibility_level']    = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
-Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root)
+Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
 Settings.gitlab['restricted_signup_domains'] ||= []
 Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git']
 
@@ -178,16 +181,21 @@ Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious'
 # CI
 #
 Settings['gitlab_ci'] ||= Settingslogic.new({})
-Settings.gitlab_ci['all_broken_builds']   = true if Settings.gitlab_ci['all_broken_builds'].nil?
-Settings.gitlab_ci['add_pusher']          = false if Settings.gitlab_ci['add_pusher'].nil?
-Settings.gitlab_ci['url']                 ||= Settings.send(:build_gitlab_ci_url)
-Settings.gitlab_ci['builds_path']         = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root)
+Settings.gitlab_ci['shared_runners_enabled'] = true if Settings.gitlab_ci['shared_runners_enabled'].nil?
+Settings.gitlab_ci['all_broken_builds']     = true if Settings.gitlab_ci['all_broken_builds'].nil?
+Settings.gitlab_ci['add_pusher']            = false if Settings.gitlab_ci['add_pusher'].nil?
+Settings.gitlab_ci['url']                   ||= Settings.send(:build_gitlab_ci_url)
+Settings.gitlab_ci['builds_path']           = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root)
 
 #
 # Reply by email
 #
 Settings['incoming_email'] ||= Settingslogic.new({})
-Settings.incoming_email['enabled'] = false if Settings.incoming_email['enabled'].nil?
+Settings.incoming_email['enabled']    = false if Settings.incoming_email['enabled'].nil?
+Settings.incoming_email['port']       = 143 if Settings.incoming_email['port'].nil?
+Settings.incoming_email['ssl']        = false if Settings.incoming_email['ssl'].nil?
+Settings.incoming_email['start_tls']  = false if Settings.incoming_email['start_tls'].nil?
+Settings.incoming_email['mailbox']    = "inbox" if Settings.incoming_email['mailbox'].nil?
 
 #
 # Gravatar
@@ -238,9 +246,12 @@ Settings.git['max_size']  ||= 20971520 # 20.megabytes
 Settings.git['bin_path']  ||= '/usr/bin/git'
 Settings.git['timeout']   ||= 10
 
+# Important: keep the satellites.path setting until GitLab 9.0 at
+# least. This setting is fed to 'rm -rf' in
+# db/migrate/20151023144219_remove_satellites.rb
 Settings['satellites'] ||= Settingslogic.new({})
 Settings.satellites['path'] = File.expand_path(Settings.satellites['path'] || "tmp/repo_satellites/", Rails.root)
-Settings.satellites['timeout'] ||= 30
+
 
 #
 # Extra customization
diff --git a/config/initializers/2_app.rb b/config/initializers/2_app.rb
index 688cdf5f4b0887eb23bcdfdf41a2c51719eb329b..35b150c9929bb656e21d89861c0e081d61174ec8 100644
--- a/config/initializers/2_app.rb
+++ b/config/initializers/2_app.rb
@@ -1,8 +1,8 @@
 module Gitlab
-  VERSION = File.read(Rails.root.join("VERSION")).strip
-  REVISION = Gitlab::Popen.popen(%W(git log --pretty=format:%h -n 1)).first.chomp
-
   def self.config
     Settings
   end
+
+  VERSION = File.read(Rails.root.join("VERSION")).strip
+  REVISION = Gitlab::Popen.popen(%W(#{config.git.bin_path} log --pretty=format:%h -n 1)).first.chomp
 end
diff --git a/config/initializers/active_record_query_trace.rb b/config/initializers/active_record_query_trace.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4b3c2803b3b36bc31df1b12500b8bc0bdad0288b
--- /dev/null
+++ b/config/initializers/active_record_query_trace.rb
@@ -0,0 +1,5 @@
+if ENV['ENABLE_QUERY_TRACE']
+  require 'active_record_query_trace'
+
+  ActiveRecordQueryTrace.enabled = 'true'
+end
diff --git a/config/initializers/bullet.rb b/config/initializers/bullet.rb
new file mode 100644
index 0000000000000000000000000000000000000000..95e82966c7ae15536f2b7dc6932f9dc45dc1d6b8
--- /dev/null
+++ b/config/initializers/bullet.rb
@@ -0,0 +1,6 @@
+if ENV['ENABLE_BULLET']
+  require 'bullet'
+
+  Bullet.enable  = true
+  Bullet.console = true
+end
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
index 5d46ece1e1b27791215a48e141697abc40860dbb..9e8b0131f8ff7e3d3923697eca0b0ffdc77b19ce 100644
--- a/config/initializers/inflections.rb
+++ b/config/initializers/inflections.rb
@@ -8,24 +8,3 @@
 #   inflect.irregular 'person', 'people'
 #   inflect.uncountable %w( fish sheep )
 # end
-
-# Mark "commits" as uncountable.
-#
-# Without this change, the routes
-#
-#   resources :commit,  only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
-#   resources :commits, only: [:show], constraints: {id: /.+/}
-#
-# would generate identical route helper methods (`project_commit_path`), resulting
-# in one of them not getting a helper method at all.
-#
-# After this change, the helper methods are:
-#
-#   project_commit_path(@project, @project.commit)
-#   # => "/gitlabhq/commit/bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a
-#
-#   project_commits_path(@project, 'stable/README.md')
-#   # => "/gitlabhq/commits/stable/README.md"
-ActiveSupport::Inflector.inflections do |inflect|
-  inflect.uncountable %w(commits)
-end
diff --git a/config/initializers/rack_lineprof.rb b/config/initializers/rack_lineprof.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f0c006d811bffe04c02572441f7d5cc329ba0255
--- /dev/null
+++ b/config/initializers/rack_lineprof.rb
@@ -0,0 +1,31 @@
+# The default colors of rack-lineprof can be very hard to look at in terminals
+# with darker backgrounds. This patch tweaks the colors a bit so the output is
+# actually readable.
+if Rails.env.development? and RUBY_ENGINE == 'ruby' and ENV['ENABLE_LINEPROF']
+  Gitlab::Application.config.middleware.use(Rack::Lineprof)
+
+  module Rack
+    class Lineprof
+      class Sample < Rack::Lineprof::Sample.superclass
+        def format(*)
+          formatted = if level == CONTEXT
+                        sprintf "                 | % 3i  %s", line, code
+                      else
+                        sprintf "% 8.1fms %5i | % 3i  %s", ms, calls, line, code
+                      end
+
+          case level
+          when CRITICAL
+            color.red formatted
+          when WARNING
+            color.yellow formatted
+          when NOMINAL
+            color.white formatted
+          else # CONTEXT
+            formatted
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/config/initializers/rack_profiler.rb b/config/initializers/rack_profiler.rb
deleted file mode 100644
index 7710eeac45395d17705422af8cc519e289efb64b..0000000000000000000000000000000000000000
--- a/config/initializers/rack_profiler.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-if Rails.env.development?
-  require 'rack-mini-profiler'
-
-  # initialization is skipped so trigger it
-  Rack::MiniProfilerRails.initialize!(Gitlab::Application)
-
-  Rack::MiniProfiler.config.position = 'right'
-  Rack::MiniProfiler.config.start_hidden = false
-  Rack::MiniProfiler.config.skip_paths << '/teaspoon'
-end
diff --git a/config/initializers/sherlock.rb b/config/initializers/sherlock.rb
new file mode 100644
index 0000000000000000000000000000000000000000..42b0d78c85fae51d6c800ab2cc66b0e77afb75a7
--- /dev/null
+++ b/config/initializers/sherlock.rb
@@ -0,0 +1,5 @@
+if Gitlab::Sherlock.enabled?
+  Gitlab::Application.configure do |config|
+    config.middleware.use(Gitlab::Sherlock::Middleware)
+  end
+end
diff --git a/config/initializers/state_machine_patch.rb b/config/initializers/state_machine_patch.rb
deleted file mode 100644
index 72d010fa5deab3f48285ad4ab07f1793f6d9c3a2..0000000000000000000000000000000000000000
--- a/config/initializers/state_machine_patch.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# This is a patch to address the issue in https://github.com/pluginaweek/state_machine/issues/251
-# where gem 'state_machine' was not working for Rails 4.1
-module StateMachine
-  module Integrations
-    module ActiveModel
-      public :around_validation
-    end
-  end
-end
diff --git a/config/locales/sherlock.en.yml b/config/locales/sherlock.en.yml
new file mode 100644
index 0000000000000000000000000000000000000000..683b09dc3294d5710850802193572470bee0f5d9
--- /dev/null
+++ b/config/locales/sherlock.en.yml
@@ -0,0 +1,37 @@
+en:
+  sherlock:
+    title: Sherlock
+    delete_all_transactions: Delete All Transactions
+    introduction: >
+      Below is a list of all transactions recorded by Sherlock. Requests to
+      Sherlock's own routes are ignored.
+    no_transactions: No transactions to show
+    no_queries: No queries to show
+    no_file_samples: No file samples to show
+    all_transactions: All Transactions
+    transaction: Transaction
+    query: Query
+    file_sample: File Sample
+    type: Type
+    path: Path
+    time: Time
+    queries: Queries
+    finished_at: Finished at
+    ago: ago
+    view: View
+    seconds: seconds
+    milliseconds: ms
+    general: General
+    id: ID
+    time_inclusive: Time (inclusive)
+    backtrace: Backtrace
+    application_backtrace: Application Backtrace
+    full_backtrace: Full Backtrace
+    origin: Origin
+    line: line
+    line_capitalized: Line
+    copy_to_clipboard: Copy to clipboard
+    query_plan: Query Plan
+    events: Events
+    percent: '%'
+    count: Count
diff --git a/config/mail_room.yml b/config/mail_room.yml
new file mode 100644
index 0000000000000000000000000000000000000000..42f6f74c465b8f1b3830b9a0e31320fc4e9ae179
--- /dev/null
+++ b/config/mail_room.yml
@@ -0,0 +1,39 @@
+:mailboxes:
+<%
+require_relative 'config/environment.rb'
+
+if Gitlab::IncomingEmail.enabled? 
+  config = Gitlab::IncomingEmail.config
+
+  redis_config_file = "config/resque.yml"
+  redis_url = 
+    if File.exists?(redis_config_file)
+      YAML.load_file(redis_config_file)[Rails.env]
+    else
+      "redis://localhost:6379"
+    end
+  %>
+  -
+    :host: <%= config.host.to_json %>
+    :port: <%= config.port.to_json %>
+    :ssl: <%= config.ssl.to_json %>
+    :start_tls: <%= config.start_tls.to_json %>
+    :email: <%= config.user.to_json %>
+    :password: <%= config.password.to_json %>
+
+    :name: <%= config.mailbox.to_json %>
+
+    :delete_after_delivery: true
+
+    :delivery_method: sidekiq
+    :delivery_options:
+      :redis_url: <%= redis_url.to_json %>
+      :namespace: resque:gitlab
+      :queue: incoming_email
+      :worker: EmailReceiverWorker
+
+    :arbitration_method: redis
+    :arbitration_options:
+      :redis_url: <%= redis_url.to_json %>
+      :namespace: mail_room:gitlab
+<% end %>
diff --git a/config/mail_room.yml.example b/config/mail_room.yml.example
deleted file mode 100644
index bb624e8a187d4bccdafabd11994d4c6b02463712..0000000000000000000000000000000000000000
--- a/config/mail_room.yml.example
+++ /dev/null
@@ -1,39 +0,0 @@
-:mailboxes:
-  -
-    # # IMAP server host
-    # :host: "imap.gmail.com"
-    # # IMAP server port
-    # :port: 993
-    # # Whether the IMAP server uses SSL
-    # :ssl: true
-    # # Whether the IMAP server uses StartTLS
-    # :start_tls: false
-    # # Email account username. Usually the full email address.
-    # :email: "gitlab-incoming@gmail.com"
-    # # Email account password
-    # :password: "password"
-
-    # # The name of the mailbox where incoming mail will end up. Usually "inbox".
-    # :name: "inbox"
-
-    # # Always "sidekiq".
-    # :delivery_method: sidekiq
-    # # Always true.
-    # :delete_after_delivery: true
-    # :delivery_options:
-    #   # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml.
-    #   :redis_url: redis://localhost:6379
-    #   # Always "resque:gitlab".
-    #   :namespace: resque:gitlab
-    #   # Always "incoming_email".
-    #   :queue: incoming_email
-    #   # Always "EmailReceiverWorker".
-    #   :worker: EmailReceiverWorker
-
-    # # Always "redis".
-    # :arbitration_method: redis
-    # :arbitration_options:
-    #   # The URL to the Redis server. Should match the URL in config/resque.yml.
-    #   :redis_url: redis://localhost:6379
-    #   # Always "mail_room:gitlab".
-    #   :namespace: mail_room:gitlab
diff --git a/config/routes.rb b/config/routes.rb
index 8e6fbf6340c0cf39e351de8eaeb4d9e2f7a44cf4..2028ea938e4579cfebec7fb6eb5dab8d7cb97a4b 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -2,6 +2,19 @@ require 'sidekiq/web'
 require 'api/api'
 
 Gitlab::Application.routes.draw do
+  if Gitlab::Sherlock.enabled?
+    namespace :sherlock do
+      resources :transactions, only: [:index, :show] do
+        resources :queries, only: [:show]
+        resources :file_samples, only: [:show]
+
+        collection do
+          delete :destroy_all
+        end
+      end
+    end
+  end
+
   namespace :ci do
     # CI API
     Ci::API::API.logger Rails.logger
@@ -23,8 +36,6 @@ Gitlab::Application.routes.draw do
       end
 
       resources :runner_projects, only: [:create, :destroy]
-
-      resources :events, only: [:index]
     end
 
     resource :user_sessions do
@@ -378,6 +389,7 @@ Gitlab::Application.routes.draw do
               [:new, :create, :index], path: "/") do
       member do
         put :transfer
+        delete :remove_fork
         post :archive
         post :unarchive
         post :toggle_star
@@ -473,8 +485,9 @@ Gitlab::Application.routes.draw do
         resources :commit, only: [:show], constraints: { id: /[[:alnum:]]{6,40}/ } do
           member do
             get :branches
-            get :ci
-            get :cancel_builds
+            get :builds
+            post :cancel_builds
+            post :retry_builds
           end
         end
 
@@ -543,8 +556,10 @@ Gitlab::Application.routes.draw do
           member do
             # tree viewer logs
             get 'logs_tree', constraints: { id: Gitlab::Regex.git_reference_regex }
+            # Directories with leading dots erroneously get rejected if git
+            # ref regex used in constraints. Regex verification now done in controller.
             get 'logs_tree/*path' => 'refs#logs_tree', as: :logs_file, constraints: {
-              id: Gitlab::Regex.git_reference_regex,
+              id: /.*/,
               path: /.*/
             }
           end
@@ -568,7 +583,10 @@ Gitlab::Application.routes.draw do
         end
 
         resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
-        resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
+        resources :tags, only: [:index, :show, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do
+          resource :release, only: [:edit, :update]
+        end
+
         resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
         resource :variables, only: [:show, :update]
         resources :triggers, only: [:index, :create, :destroy]
@@ -585,10 +603,14 @@ Gitlab::Application.routes.draw do
           end
         end
 
-        resources :builds, only: [:show] do
+        resources :builds, only: [:index, :show] do
+          collection do
+            post :cancel_all
+          end
+
           member do
-            get :cancel
             get :status
+            post :cancel
             post :retry
           end
         end
diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb
index 378354efd5a056467f90d36674d4d13c9d09089a..03da29c4c686930c5020b6fac27400b204a485aa 100644
--- a/db/fixtures/development/05_users.rb
+++ b/db/fixtures/development/05_users.rb
@@ -1,5 +1,5 @@
 Gitlab::Seeder.quiet do
-  (2..20).each  do |i|
+  20.times do |i|
     begin
       User.create!(
         username: FFaker::Internet.user_name,
@@ -15,7 +15,7 @@ Gitlab::Seeder.quiet do
     end
   end
 
-  (1..5).each do |i|
+  5.times do |i|
     begin
       User.create!(
         username: "user#{i}",
diff --git a/db/fixtures/development/07_milestones.rb b/db/fixtures/development/07_milestones.rb
index a43116829d978032bbae30c695ed120c590cfc67..e028ac82ba33898e1f0185fd47845ec764e72fe9 100644
--- a/db/fixtures/development/07_milestones.rb
+++ b/db/fixtures/development/07_milestones.rb
@@ -1,6 +1,6 @@
 Gitlab::Seeder.quiet do
   Project.all.each do |project|
-    (1..5).each  do |i|
+    5.times do |i|
       milestone_params = {
         title: "v#{i}.0",
         description: FFaker::Lorem.sentence,
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index c636e96381ca4f58d24b4c806e98a1117c8263a4..4fa572fca9bdab3570781c49c95954b7dc1f164a 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -1,6 +1,6 @@
 Gitlab::Seeder.quiet do
   Project.all.each do |project|
-    (1..10).each  do |i|
+    10.times do
       issue_params = {
         title: FFaker::Lorem.sentence(6),
         description: FFaker::Lorem.sentence,
diff --git a/db/fixtures/development/12_snippets.rb b/db/fixtures/development/12_snippets.rb
index 3bd4b442adeaa1fee3418b45f52d8f8734be1df9..74898544a69a49c6656ba17669392ed00743d490 100644
--- a/db/fixtures/development/12_snippets.rb
+++ b/db/fixtures/development/12_snippets.rb
@@ -22,7 +22,7 @@ class Member < ActiveRecord::Base
 end
 eos
 
-  (1..50).each  do |i|
+  50.times do |i|
     user = User.all.sample
 
     PersonalSnippet.seed(:id, [{
diff --git a/db/migrate/20151008110232_add_users_lower_username_email_indexes.rb b/db/migrate/20151008110232_add_users_lower_username_email_indexes.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2f2dc7767855c9afd0dc85bf7429f09b2af9b2da
--- /dev/null
+++ b/db/migrate/20151008110232_add_users_lower_username_email_indexes.rb
@@ -0,0 +1,17 @@
+class AddUsersLowerUsernameEmailIndexes < ActiveRecord::Migration
+  disable_ddl_transaction!
+
+  def up
+    return unless Gitlab::Database.postgresql?
+
+    execute 'CREATE INDEX CONCURRENTLY index_on_users_lower_username ON users (LOWER(username));'
+    execute 'CREATE INDEX CONCURRENTLY index_on_users_lower_email ON users (LOWER(email));'
+  end
+
+  def down
+    return unless Gitlab::Database.postgresql?
+
+    remove_index :users, :index_on_users_lower_username
+    remove_index :users, :index_on_users_lower_email
+  end
+end
diff --git a/db/migrate/20151008143519_add_admin_notification_email_setting.rb b/db/migrate/20151008143519_add_admin_notification_email_setting.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0bb581efe2c56d70d3041c17c065b7e631b00a11
--- /dev/null
+++ b/db/migrate/20151008143519_add_admin_notification_email_setting.rb
@@ -0,0 +1,5 @@
+class AddAdminNotificationEmailSetting < ActiveRecord::Migration
+  def change
+    add_column :application_settings, :admin_notification_email, :string
+  end
+end
diff --git a/db/migrate/20151016131433_add_ci_projects_gl_project_id_index.rb b/db/migrate/20151016131433_add_ci_projects_gl_project_id_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..52a47aa9c54debafcad6fd7e5936b6380a01cf48
--- /dev/null
+++ b/db/migrate/20151016131433_add_ci_projects_gl_project_id_index.rb
@@ -0,0 +1,5 @@
+class AddCiProjectsGlProjectIdIndex < ActiveRecord::Migration
+  def change
+    add_index :ci_commits, :gl_project_id
+  end
+end
diff --git a/db/migrate/20151016195451_add_ci_builds_and_projects_indexes.rb b/db/migrate/20151016195451_add_ci_builds_and_projects_indexes.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7f1af1c758307f65a568079ddbe7aa9e635cc4e2
--- /dev/null
+++ b/db/migrate/20151016195451_add_ci_builds_and_projects_indexes.rb
@@ -0,0 +1,9 @@
+class AddCiBuildsAndProjectsIndexes < ActiveRecord::Migration
+  def change
+    add_index :ci_projects, :gitlab_id
+    add_index :ci_projects, :shared_runners_enabled
+
+    add_index :ci_builds, :type
+    add_index :ci_builds, :status
+  end
+end
diff --git a/db/migrate/20151016195706_add_notes_line_code_index.rb b/db/migrate/20151016195706_add_notes_line_code_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aeeb1a759fab2aef1b184d24b683052647c643b2
--- /dev/null
+++ b/db/migrate/20151016195706_add_notes_line_code_index.rb
@@ -0,0 +1,5 @@
+class AddNotesLineCodeIndex < ActiveRecord::Migration
+  def change
+    add_index :notes, :line_code
+  end
+end
diff --git a/db/migrate/20151019111551_fix_build_tags.rb b/db/migrate/20151019111551_fix_build_tags.rb
new file mode 100644
index 0000000000000000000000000000000000000000..299a24b0a7c5a75ce171f4cd5bf5765ee0336ac2
--- /dev/null
+++ b/db/migrate/20151019111551_fix_build_tags.rb
@@ -0,0 +1,9 @@
+class FixBuildTags < ActiveRecord::Migration
+  def up
+    execute("UPDATE taggings SET taggable_type='CommitStatus' WHERE taggable_type='Ci::Build'")
+  end
+
+  def down
+    execute("UPDATE taggings SET taggable_type='Ci::Build' WHERE taggable_type='CommitStatus'")
+  end
+end
diff --git a/db/migrate/20151019111703_fail_build_without_names.rb b/db/migrate/20151019111703_fail_build_without_names.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dcdb5d1b25ddcd5adb0744ce9d143b8814d4ea02
--- /dev/null
+++ b/db/migrate/20151019111703_fail_build_without_names.rb
@@ -0,0 +1,8 @@
+class FailBuildWithoutNames < ActiveRecord::Migration
+  def up
+    execute("UPDATE ci_builds SET status='failed' WHERE name IS NULL AND status='pending'")
+  end
+
+  def down
+  end
+end
diff --git a/db/migrate/20151020145526_add_services_template_index.rb b/db/migrate/20151020145526_add_services_template_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1b04f313565dafc7327403381a509bbefd129d05
--- /dev/null
+++ b/db/migrate/20151020145526_add_services_template_index.rb
@@ -0,0 +1,5 @@
+class AddServicesTemplateIndex < ActiveRecord::Migration
+  def change
+    add_index :services, :template
+  end
+end
diff --git a/db/migrate/20151020173516_ci_limits_to_mysql.rb b/db/migrate/20151020173516_ci_limits_to_mysql.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9bb960082f58befe42999d26e654750eae9db8c9
--- /dev/null
+++ b/db/migrate/20151020173516_ci_limits_to_mysql.rb
@@ -0,0 +1,9 @@
+class CiLimitsToMysql < ActiveRecord::Migration
+  def change
+    return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
+
+    # CI
+    change_column :ci_builds, :trace, :text, limit: 1073741823
+    change_column :ci_commits, :push_data, :text, limit: 16777215
+  end
+end
diff --git a/db/migrate/20151020173906_add_ci_builds_index_for_status.rb b/db/migrate/20151020173906_add_ci_builds_index_for_status.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c3f0e0606dab6b46be92e8993a3b815aea52f4af
--- /dev/null
+++ b/db/migrate/20151020173906_add_ci_builds_index_for_status.rb
@@ -0,0 +1,5 @@
+class AddCiBuildsIndexForStatus < ActiveRecord::Migration
+  def change
+    add_index :ci_builds, [:commit_id, :status, :type]
+  end
+end
diff --git a/db/migrate/20151023112551_fail_build_with_empty_name.rb b/db/migrate/20151023112551_fail_build_with_empty_name.rb
new file mode 100644
index 0000000000000000000000000000000000000000..41c0f0649cd6192211e9a219abf24eba2f562fd0
--- /dev/null
+++ b/db/migrate/20151023112551_fail_build_with_empty_name.rb
@@ -0,0 +1,8 @@
+class FailBuildWithEmptyName < ActiveRecord::Migration
+  def up
+    execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'")
+  end
+
+  def down
+  end
+end
diff --git a/db/migrate/20151023144219_remove_satellites.rb b/db/migrate/20151023144219_remove_satellites.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e73f300028ac8a2f2c47d0b9ae5e42c16e286834
--- /dev/null
+++ b/db/migrate/20151023144219_remove_satellites.rb
@@ -0,0 +1,17 @@
+require 'fileutils'
+
+class RemoveSatellites < ActiveRecord::Migration
+  def up
+    satellites = Gitlab.config['satellites']
+    return if satellites.nil?
+
+    satellites_path = satellites['path']
+    return if satellites_path.nil?
+
+    FileUtils.rm_rf(satellites_path)
+  end
+
+  def down
+    # Do nothing
+  end
+end
diff --git a/db/migrate/20151026182941_add_project_path_index.rb b/db/migrate/20151026182941_add_project_path_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a62fe199d70e75349d4b573785396c7f72eb192c
--- /dev/null
+++ b/db/migrate/20151026182941_add_project_path_index.rb
@@ -0,0 +1,9 @@
+class AddProjectPathIndex < ActiveRecord::Migration
+  def up
+    add_index :projects, :path
+  end
+
+  def down
+    remove_index :projects, :path
+  end
+end
diff --git a/db/migrate/20151103001141_add_public_to_group.rb b/db/migrate/20151103001141_add_public_to_group.rb
new file mode 100644
index 0000000000000000000000000000000000000000..635346300c2d17fa92adedf866f489e8ec4aebd3
--- /dev/null
+++ b/db/migrate/20151103001141_add_public_to_group.rb
@@ -0,0 +1,5 @@
+class AddPublicToGroup < ActiveRecord::Migration
+  def change
+    add_column :namespaces, :public, :boolean, default: false
+  end
+end
diff --git a/db/migrate/20151103133339_add_shared_runners_setting.rb b/db/migrate/20151103133339_add_shared_runners_setting.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4231dfd5c2e81a00f4ffd2f41acf00dd5fc59137
--- /dev/null
+++ b/db/migrate/20151103133339_add_shared_runners_setting.rb
@@ -0,0 +1,5 @@
+class AddSharedRunnersSetting < ActiveRecord::Migration
+  def up
+    add_column :application_settings, :shared_runners_enabled, :boolean, default: true, null: false
+  end
+end
diff --git a/db/migrate/20151105094515_create_releases.rb b/db/migrate/20151105094515_create_releases.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fe4608c6662a27da1bba76be77729dce46d88f19
--- /dev/null
+++ b/db/migrate/20151105094515_create_releases.rb
@@ -0,0 +1,14 @@
+class CreateReleases < ActiveRecord::Migration
+  def change
+    create_table :releases do |t|
+      t.string :tag
+      t.text :description
+      t.integer :project_id
+
+      t.timestamps
+    end
+
+    add_index :releases, :project_id
+    add_index :releases, [:project_id, :tag]
+  end
+end
diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb
index 73605d4c5e3096900949f2bb2287ae6c3e3e3a7b..2b7afae6d7cb3f73197f2c6d9549ba216429af82 100644
--- a/db/migrate/limits_to_mysql.rb
+++ b/db/migrate/limits_to_mysql.rb
@@ -6,9 +6,5 @@ class LimitsToMysql < ActiveRecord::Migration
     change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
     change_column :snippets, :content, :text, limit: 2147483647
     change_column :notes, :st_diff, :text, limit: 2147483647
-
-    # CI
-    change_column :ci_builds, :trace, :text, limit: 1073741823
-    change_column :ci_commits, :push_data, :text, limit: 16777215
   end
 end
diff --git a/db/schema.rb b/db/schema.rb
index 7a11dfca03422fcc090afbc39ef469dff12191dd..116c0c8d97d63620f424b3edc3ed07d2bf799261 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20151008130321) do
+ActiveRecord::Schema.define(version: 20151105094515) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -46,6 +46,8 @@ ActiveRecord::Schema.define(version: 20151008130321) do
     t.integer  "session_expire_delay",         default: 10080, null: false
     t.text     "import_sources"
     t.text     "help_page_text"
+    t.string   "admin_notification_email"
+    t.boolean  "shared_runners_enabled",       default: true,  null: false
   end
 
   create_table "audit_events", force: true do |t|
@@ -109,12 +111,15 @@ ActiveRecord::Schema.define(version: 20151008130321) do
   end
 
   add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
+  add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
   add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
   add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
   add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree
   add_index "ci_builds", ["project_id", "commit_id"], name: "index_ci_builds_on_project_id_and_commit_id", using: :btree
   add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
   add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
+  add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree
+  add_index "ci_builds", ["type"], name: "index_ci_builds_on_type", using: :btree
 
   create_table "ci_commits", force: true do |t|
     t.integer  "project_id"
@@ -130,6 +135,7 @@ ActiveRecord::Schema.define(version: 20151008130321) do
     t.integer  "gl_project_id"
   end
 
+  add_index "ci_commits", ["gl_project_id"], name: "index_ci_commits_on_gl_project_id", using: :btree
   add_index "ci_commits", ["project_id", "committed_at", "id"], name: "index_ci_commits_on_project_id_and_committed_at_and_id", using: :btree
   add_index "ci_commits", ["project_id", "committed_at"], name: "index_ci_commits_on_project_id_and_committed_at", using: :btree
   add_index "ci_commits", ["project_id", "sha"], name: "index_ci_commits_on_project_id_and_sha", using: :btree
@@ -189,6 +195,9 @@ ActiveRecord::Schema.define(version: 20151008130321) do
     t.text     "generated_yaml_config"
   end
 
+  add_index "ci_projects", ["gitlab_id"], name: "index_ci_projects_on_gitlab_id", using: :btree
+  add_index "ci_projects", ["shared_runners_enabled"], name: "index_ci_projects_on_shared_runners_enabled", using: :btree
+
   create_table "ci_runner_projects", force: true do |t|
     t.integer  "runner_id",  null: false
     t.integer  "project_id", null: false
@@ -493,14 +502,15 @@ ActiveRecord::Schema.define(version: 20151008130321) do
   add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree
 
   create_table "namespaces", force: true do |t|
-    t.string   "name",                     null: false
-    t.string   "path",                     null: false
+    t.string   "name",                        null: false
+    t.string   "path",                        null: false
     t.integer  "owner_id"
     t.datetime "created_at"
     t.datetime "updated_at"
     t.string   "type"
-    t.string   "description", default: "", null: false
+    t.string   "description", default: "",    null: false
     t.string   "avatar"
+    t.boolean  "public",      default: false
   end
 
   add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree
@@ -529,6 +539,7 @@ ActiveRecord::Schema.define(version: 20151008130321) do
   add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree
   add_index "notes", ["created_at", "id"], name: "index_notes_on_created_at_and_id", using: :btree
   add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree
+  add_index "notes", ["line_code"], name: "index_notes_on_line_code", using: :btree
   add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
   add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
   add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
@@ -615,6 +626,7 @@ ActiveRecord::Schema.define(version: 20151008130321) do
   add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
   add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
   add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
+  add_index "projects", ["path"], name: "index_projects_on_path", using: :btree
   add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree
 
   create_table "protected_branches", force: true do |t|
@@ -627,6 +639,17 @@ ActiveRecord::Schema.define(version: 20151008130321) do
 
   add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
 
+  create_table "releases", force: true do |t|
+    t.string   "tag"
+    t.text     "description"
+    t.integer  "project_id"
+    t.datetime "created_at"
+    t.datetime "updated_at"
+  end
+
+  add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree
+  add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree
+
   create_table "sent_notifications", force: true do |t|
     t.integer "project_id"
     t.integer "noteable_id"
@@ -657,6 +680,7 @@ ActiveRecord::Schema.define(version: 20151008130321) do
 
   add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree
   add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
+  add_index "services", ["template"], name: "index_services_on_template", using: :btree
 
   create_table "snippets", force: true do |t|
     t.string   "title"
diff --git a/doc/README.md b/doc/README.md
index a0ff856ebb639ec6204c4ac49f4b62642664b3fc..0f6866475f716b77b8f7a095f6af0db6d7c78661 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -17,20 +17,22 @@
 
 ## CI Documentation
 
-+ [Quick Start](ci/quick_start/README.md)
-+ [Configuring project (.gitlab-ci.yml)](ci/yaml/README.md)
-+ [Configuring runner](ci/runners/README.md)
-+ [Configuring deployment](ci/deployment/README.md)
-+ [Using Docker Images](ci/docker/using_docker_images.md)
-+ [Using Docker Build](ci/docker/using_docker_build.md)
-+ [Using Variables](ci/variables/README.md)
+- [Quick Start](ci/quick_start/README.md)
+- [Configuring project (.gitlab-ci.yml)](ci/yaml/README.md)
+- [Configuring runner](ci/runners/README.md)
+- [Configuring deployment](ci/deployment/README.md)
+- [Using Docker Images](ci/docker/using_docker_images.md)
+- [Using Docker Build](ci/docker/using_docker_build.md)
+- [Using Variables](ci/variables/README.md)
+- [User permissions](ci/permissions/README.md)
+- [API](ci/api/README.md)
 
 ### CI Examples
 
-+ [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md)
-+ [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md)
-+ [Test Clojure applications](ci/examples/test-clojure-application.md)
-+ Help your favorite programming language and GitLab by sending a merge request with a guide for that language.
+- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md)
+- [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md)
+- [Test Clojure applications](ci/examples/test-clojure-application.md)
+- Help your favorite programming language and GitLab by sending a merge request with a guide for that language.
 
 ## Administrator documentation
 
@@ -49,11 +51,6 @@
 - [Reply by email](incoming_email/README.md) Allow users to comment on issues and merge requests by replying to notification emails.
 - [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
 
-### Administrator documentation
-
-+ [User permissions](permissions/permissions.md)
-+ [API](api/README.md)
-
 ## Contributor documentation
 
 - [Development](development/README.md) Explains the architecture and the guidelines for shell commands.
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 9f72adc6ed9ac22a6233ecb4d7e0bb30465f6bac..8e4a0ee1b82b4e2e53963c74001808bd4ca549a4 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -22,7 +22,8 @@ Parameters:
     "author_name": "Dmitriy Zaporozhets",
     "author_email": "dzaporozhets@sphereconsultinginc.com",
     "created_at": "2012-09-20T11:50:22+03:00",
-    "message": "Replace sanitize with escape once"
+    "message": "Replace sanitize with escape once",
+    "allow_failure": false
   },
   {
     "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
@@ -31,7 +32,8 @@ Parameters:
     "author_name": "randx",
     "author_email": "dmitriy.zaporozhets@gmail.com",
     "created_at": "2012-09-20T09:06:12+03:00",
-    "message": "Sanitize for network graph"
+    "message": "Sanitize for network graph",
+    "allow_failure": false
   }
 ]
 ```
diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md
index 25311b071076454a994eef8a5d1d61d272520dc1..623063f357b5cdde9aab9237b3507475190cc25f 100644
--- a/doc/api/repository_files.md
+++ b/doc/api/repository_files.md
@@ -23,7 +23,8 @@ Example response:
   "content": "IyA9PSBTY2hlbWEgSW5mb3...",
   "ref": "master",
   "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
-  "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50"
+  "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50",
+  "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d"
 }
 ```
 
diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md
index 33c5b172e988fe9223483bd6a0723c748f1234a8..cf9710ede577759608e4bbb28dfa802d4598e283 100644
--- a/doc/ci/api/README.md
+++ b/doc/ci/api/README.md
@@ -25,7 +25,7 @@ GitLab CI API has 4 authentication methods:
 
 Authentication is done by
 sending the `private-token` of a valid user and the `url` of an
-authorized Gitlab instance via a query string along with the API
+authorized GitLab instance via a query string along with the API
 request:
 
     GET http://gitlab.example.com/ci/api/v1/projects?private_token=QVy1PB7sTxfy4pqfZM1U&url=http://demo.gitlab.com/
diff --git a/doc/ci/api/projects.md b/doc/ci/api/projects.md
index 5585191e8269b340a92b859ae957e023f7072456..74a4c64d0003a39d0d64d0a4d036c31c36d9c8be 100644
--- a/doc/ci/api/projects.md
+++ b/doc/ci/api/projects.md
@@ -1,7 +1,7 @@
 # Projects API
 
 This API is intended to aid in the setup and configuration of
-projects on Gitlab CI. 
+projects on GitLab CI.
 
 __Authentication is done by GitLab user token & GitLab url__
 
@@ -88,23 +88,23 @@ authorized.
 
 Parameters:
 
-  * `id` (required) - The ID of the Gitlab CI project
+  * `id` (required) - The ID of the GitLab CI project
 
 ### Create Project
 
-Creates a Gitlab CI project using Gitlab project details.
+Creates a GitLab CI project using GitLab project details.
 
     POST /ci/projects
 
 Parameters:
 
   * `name` (required) - The name of the project
-  * `gitlab_id` (required) - The ID of the project on the Gitlab instance
+  * `gitlab_id` (required) - The ID of the project on the GitLab instance
   * `default_ref` (optional) - The branch to run on (default to `master`)
 
 ### Update Project
 
-Updates a Gitlab CI project using Gitlab project details that the
+Updates a GitLab CI project using GitLab project details that the
 authenticated user has access to.
 
     PUT /ci/projects/:id
@@ -116,13 +116,13 @@ Parameters:
 
 ### Remove Project
 
-Removes a Gitlab CI project that the authenticated user has access to.
+Removes a GitLab CI project that the authenticated user has access to.
 
     DELETE /ci/projects/:id
 
 Parameters:
 
-  * `id` (required) - The ID of the Gitlab CI project
+  * `id` (required) - The ID of the GitLab CI project
 
 ### Link Project to Runner
 
@@ -133,8 +133,8 @@ authorized user).
 
 Parameters:
 
-  * `id` (required) - The ID of the Gitlab CI project
-  * `runner_id` (required) - The ID of the Gitlab CI runner
+  * `id` (required) - The ID of the GitLab CI project
+  * `runner_id` (required) - The ID of the GitLab CI runner
 
 ### Remove Project from Runner
 
@@ -145,5 +145,5 @@ via authorized user).
 
 Parameters:
 
-  * `id` (required) - The ID of the Gitlab CI project
-  * `runner_id` (required) - The ID of the Gitlab CI runner
\ No newline at end of file
+  * `id` (required) - The ID of the GitLab CI project
+  * `runner_id` (required) - The ID of the GitLab CI runner
\ No newline at end of file
diff --git a/doc/ci/api/runners.md b/doc/ci/api/runners.md
index e9f88ee066eb665310b4959314c82537b883a924..c383dc4bcc94dbab361d538c8b9645cd4188a96b 100644
--- a/doc/ci/api/runners.md
+++ b/doc/ci/api/runners.md
@@ -6,7 +6,7 @@
 
 __Authentication is done by GitLab user token & GitLab url__
 
-Used to get information about all runners registered on the Gitlab CI
+Used to get information about all runners registered on the GitLab CI
 instance.
 
     GET /ci/runners
@@ -31,7 +31,7 @@ Returns:
 
 __Authentication is done with a Shared runner registration token or a project Specific runner registration token__
 
-Used to make Gitlab CI aware of available runners.
+Used to make GitLab CI aware of available runners.
 
     POST /ci/runners/register
 
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 191e3a8144d2601c8d53501b0ccaae5b81db353e..ef8a7ec1e8613c45d1002b68999b1b0350ae09fa 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -90,7 +90,7 @@ you need to set MYSQL_ALLOW_EMPTY_PASSWORD.
     - mysql
     
     variables:
-      MYSQL_ALLOW_EMPTY_PASSWORD: yes
+      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
     ```
 
 For other possible configuration variables check the 
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index e0b9fa0e25df3460f3ded2d58fc7e7642e746392..1cf41aea391f32c4af004c5aed31f2456b486b7c 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -1,5 +1,5 @@
 # Build script examples
 
-+ [Test and deploy Ruby Application to Heroku](test-and-deploy-ruby-application-to-heroku.md)
-+ [Test and deploy Python Application to Heroku](test-and-deploy-python-application-to-heroku.md)
-+ [Test Clojure applications](examples/test-clojure-application.md)
++ [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md)
++ [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md)
++ [Test a Clojure application](test-clojure-application.md)
diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
index 036b03dd6b922dbba270ab159e13f0b5ed44a793..a236da53fe9e03907d694f05f90c4913228f4430 100644
--- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
@@ -1,7 +1,7 @@
 ## Test and Deploy a python application
 This example will guide you how to run tests in your Python application and deploy it automatically as Heroku application.
 
-You can checkout the example [source](https://gitlab.com/ayufan/python-getting-started) and check [CI status](https://ci.gitlab.com/projects/4080).
+You can checkout the example [source](https://gitlab.com/ayufan/python-getting-started) and check [CI status](https://gitlab.com/ayufan/python-getting-started/builds?scope=all).
 
 ### Configure project
 This is what the `.gitlab-ci.yml` file looks like for this project:
diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
index d2a872f1934895e321c625c644c1ae483aa6db1f..e52e1547461d974306cc0393a053f86685dcdbe9 100644
--- a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
@@ -1,7 +1,7 @@
 ## Test and Deploy a ruby application
 This example will guide you how to run tests in your Ruby application and deploy it automatiacally as Heroku application.
 
-You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://ci.gitlab.com/projects/4050).
+You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://gitlab.com/ayufan/ruby-getting-started/builds?scope=all).
 
 ### Configure project
 This is what the `.gitlab-ci.yml` file looks like for this project:
@@ -64,4 +64,4 @@ gitlab-ci-multi-runner register \
 
 With the command above, you create a runner that uses [ruby:2.1](https://registry.hub.docker.com/u/library/ruby/) image and uses [postgres](https://registry.hub.docker.com/u/library/postgres/) database.
 
-To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password.
\ No newline at end of file
+To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password.
diff --git a/doc/ci/examples/test-clojure-application.md b/doc/ci/examples/test-clojure-application.md
index eaee94a10f13ccac2677ff76b423a8b06821f930..56b746ce0252b9e25d41a77ef86bcd2104b66bbb 100644
--- a/doc/ci/examples/test-clojure-application.md
+++ b/doc/ci/examples/test-clojure-application.md
@@ -1,8 +1,8 @@
-## Test Clojure applications
+## Test a Clojure application
 
 This example will guide you how to run tests in your Clojure application.
 
-You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://ci.gitlab.com/projects/6306).
+You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://gitlab.com/dzaporozhets/clojure-web-application/builds?scope=all).
 
 ### Configure project
 
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 04c6bf1e3a3a7037200b1a91acb265ead531a71a..022afb700424c25eab121cc08d00e667d4999e7f 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -15,21 +15,27 @@ The API_TOKEN will take the Secure Variable value: `SECURE`.
 
 ### Predefined variables (Environment Variables)
 
-| Variable                | Description |
+| Variable                | Runner | Description |
 |-------------------------|-------------|
-| **CI**                  | Mark that build is executed in CI environment |
-| **GITLAB_CI**           | Mark that build is executed in GitLab CI environment |
-| **CI_SERVER**           | Mark that build is executed in CI environment |
-| **CI_SERVER_NAME**      | CI server that is used to coordinate builds |
-| **CI_SERVER_VERSION**   | Not yet defined |
-| **CI_SERVER_REVISION**  | Not yet defined |
-| **CI_BUILD_REF**        | The commit revision for which project is built |
-| **CI_BUILD_BEFORE_SHA** | The first commit that were included in push request |
-| **CI_BUILD_REF_NAME**   | The branch or tag name for which project is built |
-| **CI_BUILD_ID**         | The unique id of the current build that GitLab CI uses internally |
-| **CI_BUILD_REPO**       | The URL to clone the Git repository |
-| **CI_PROJECT_ID**       | The unique id of the current project that GitLab CI uses internally |
-| **CI_PROJECT_DIR**      | The full path where the repository is cloned and where the build is ran |
+| **CI**                  | 0.4 | Mark that build is executed in CI environment |
+| **GITLAB_CI**           | all | Mark that build is executed in GitLab CI environment |
+| **CI_SERVER**           | all | Mark that build is executed in CI environment |
+| **CI_SERVER_NAME**      | all | CI server that is used to coordinate builds |
+| **CI_SERVER_VERSION**   | all | Not yet defined |
+| **CI_SERVER_REVISION**  | all | Not yet defined |
+| **CI_BUILD_REF**        | all | The commit revision for which project is built |
+| **CI_BUILD_TAG**        | 0.5 | The commit tag name. Present only when building tags. |
+| **CI_BUILD_NAME**       | 0.5 | The name of the build as defined in `.gitlab-ci.yml` |
+| **CI_BUILD_STAGE**      | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
+| **CI_BUILD_BEFORE_SHA** | all | The first commit that were included in push request |
+| **CI_BUILD_REF_NAME**   | all | The branch or tag name for which project is built |
+| **CI_BUILD_ID**         | all | The unique id of the current build that GitLab CI uses internally |
+| **CI_BUILD_REPO**       | all | The URL to clone the Git repository |
+| **CI_BUILD_TRIGGERED**  | 0.5 | The flag to indicate that build was triggered |
+| **CI_PROJECT_ID**       | all | The unique id of the current project that GitLab CI uses internally |
+| **CI_PROJECT_DIR**      | all | The full path where the repository is cloned and where the build is ran |
+
+**Some of the variables are only available when using runner with at least defined version.**
 
 Example values:
 
@@ -39,6 +45,10 @@ export CI_BUILD_ID="50"
 export CI_BUILD_REF="1ecfd275763eff1d6b4844ea3168962458c9f27a"
 export CI_BUILD_REF_NAME="master"
 export CI_BUILD_REPO="https://gitlab.com/gitlab-org/gitlab-ce.git"
+export CI_BUILD_TAG="1.0.0"
+export CI_BUILD_NAME="spec:other"
+export CI_BUILD_STAGE="test"
+export CI_BUILD_TRIGGERED="true"
 export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce"
 export CI_PROJECT_ID="34"
 export CI_SERVER="yes"
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 4caeccacb7f94d1414aaecd6a53f658acfa61de9..d117a2969be1b3d643004a7c0efd2d7138036a51 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -140,6 +140,7 @@ job_name:
 | except        | optional | Defines a list of git refs for which build is not created |
 | tags          | optional | Defines a list of tags which are used to select runner |
 | allow_failure | optional | Allow build to fail. Failed build doesn't contribute to commit status |
+| when          | optional | Define when to run build. Can be `on_success`, `on_failure` or `always` |
 
 ### script
 `script` is a shell script which is executed by runner. The shell script is prepended with `before_script`.
@@ -168,7 +169,7 @@ This are two parameters that allow for setting a refs policy to limit when jobs
 
 There are a few rules that apply to usage of refs policy:
 
-1. `only` and `except` are exclusive. If both `only` and `except` are defined in job specification only `only` is taken into account.
+1. `only` and `except` are inclusive. If both `only` and `except` are defined in job specification the ref is filtered by `only` and `except`.
 1. `only` and `except` allow for using the regexp expressions.
 1. `only` and `except` allow for using special keywords: `branches` and `tags`.
 These names can be used for example to exclude all tags and all branches.
@@ -181,6 +182,18 @@ job:
     - branches # use special keyword
 ```
 
+1. `only` and `except` allow for specify repository path to filter jobs for forks.
+The repository path can be used to have jobs executed only for parent repository.
+
+```yaml
+job:
+  only:
+    - branches@gitlab-org/gitlab-ce
+  except:
+    - master@gitlab-org/gitlab-ce
+```
+The above will run `job` for all branches on `gitlab-org/gitlab-ce`, except master .
+
 ### tags
 `tags` is used to select specific runners from the list of all runners that are allowed to run this project.
 
@@ -196,9 +209,58 @@ job:
 
 The above specification will make sure that `job` is built by a runner that have `ruby` AND `postgres` tags defined.
 
+### when
+`when` is used to implement jobs that are run in case of failure or despite the failure.
+
+`when` can be set to one of the following values:
+
+1. `on_success` - execute build only when all builds from prior stages succeeded. This is the default.
+1. `on_failure` - execute build only when at least one build from prior stages failed.
+1. `always` - execute build despite the status of builds from prior stages.
+
+```
+stages:
+- build
+- cleanup_build
+- test
+- deploy
+- cleanup
+
+build:
+  stage: build
+  script:
+  - make build
+
+cleanup_build:
+  stage: cleanup_build
+  script:
+  - cleanup build when failed
+  when: on_failure
+
+test:
+  stage: test
+  script:
+  - make test
+
+deploy:
+  stage: deploy
+  script:
+  - make deploy
+
+cleanup:
+  stage: cleanup
+  script:
+  - cleanup after builds
+  when: always
+```
+
+The above script will:
+1. Execute `cleanup_build` only when the `build` failed,
+2. Always execute `cleanup` as the last step in pipeline.
+
 ## Validate the .gitlab-ci.yml
 Each instance of GitLab CI has an embedded debug tool called Lint.
 You can find the link to the Lint in the project's settings page or use short url `/lint`.
 
 ## Skipping builds
-There is one more way to skip all builds, if your commit message contains tag [ci skip]. In this case, commit will be created but builds will be skipped
\ No newline at end of file
+There is one more way to skip all builds, if your commit message contains tag [ci skip]. In this case, commit will be created but builds will be skipped
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
new file mode 100644
index 0000000000000000000000000000000000000000..e244ad4e881d6caae77437e8f0d515f81a33f8cc
--- /dev/null
+++ b/doc/development/profiling.md
@@ -0,0 +1,27 @@
+# Profiling
+
+To make it easier to track down performance problems GitLab comes with a set of
+profiling tools, some of these are available by default while others need to be
+explicitly enabled.
+
+## Sherlock
+
+Sherlock is a custom profiling tool built into GitLab. Sherlock is _only_
+available when running GitLab in development mode _and_ when setting the
+environment variable `ENABLE_SHERLOCK` to a non empty value. For example:
+
+    ENABLE_SHERLOCK=1 bundle exec rails s
+
+Recorded transactions can be found by navigating to `/sherlock/transactions`.
+
+## Bullet
+
+Bullet is a Gem that can be used to track down N+1 query problems. Because
+Bullet adds quite a bit of logging noise it's disabled by default. To enable
+Bullet, set the environment variable `ENABLE_BULLET` to a non-empty value before
+starting GitLab. For example:
+
+    ENABLE_BULLET=true bundle exec rails s
+
+Bullet will log query problems to both the Rails log as well as the Chrome
+console.
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index a4a980cf0e0a60bfb518ce37d641fadc82517224..9f3fd69fc4eeef4e163337d101e2ea4f8c0fa590 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -9,7 +9,7 @@ bundle exec rake setup
 ```
 
 The `setup` task is a alias for `gitlab:setup`.
-This tasks calls `db:setup` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and fianlly it calls `db:seed_fu` to seed the database.
+This tasks calls `db:setup` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and finally it calls `db:seed_fu` to seed the database.
 Note: `db:setup` calls `db:seed` but this does nothing.
 
 ## Run tests
diff --git a/doc/development/shared_files.md b/doc/development/shared_files.md
new file mode 100644
index 0000000000000000000000000000000000000000..fcd905b54a43f5bc613eaeae3c25d9de884db74c
--- /dev/null
+++ b/doc/development/shared_files.md
@@ -0,0 +1,33 @@
+# Shared files
+
+Historically, GitLab has been storing shared files in many different
+directories: `public/uploads`, `builds`, `tmp/repositories`, `tmp/rebase` (EE),
+etc. Having so many shared directories makes it difficult to deploy GitLab on
+shared storage (e.g. NFS). Working towards GitLab 9.0 we are consolidating
+these different directories under the `shared` directory.
+
+This means that if GitLab will start storing puppies in some future version
+then we should put them in `shared/puppies`. Temporary puppy files should be
+stored in `shared/tmp`.
+
+In the GitLab application code you can get the full path to the `shared`
+directory with `Gitlab.config.shared.path`.
+
+## What is not a 'shared file'
+
+Files that belong to only one process, or on only one server, should not go in
+`shared`. Examples include PID files and sockets.
+
+## Temporary files and shared storage
+
+Sometimes you create a temporary file on disk with the intention of it becoming
+'official'. For example you might be first streaming an upload from a user to
+disk in a temporary file so you can perform some checks on it. When the checks
+pass, you make the file official. In scenarios like this please follow these
+rules:
+
+- Store the temporary file under `shared/tmp`, i.e. on the same filesystem you
+  want the official file to be on.
+- Use move/rename operations when operating on the file instead of copy
+  operations where possible, because renaming a file is much faster than
+  copying it.
diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md
index 2d1d0fb415445597d19f09c0bea1ad5ed5e59284..65cdd74bdb6ab9d6980f21a0dc7aa7e3065745f3 100644
--- a/doc/development/shell_commands.md
+++ b/doc/development/shell_commands.md
@@ -35,6 +35,16 @@ Gitlab::Popen.popen(%W(find /some/path -not -path /some/path -mmin +120 -delete)
 
 This coding style could have prevented CVE-2013-4490.
 
+## Always use the configurable git binary path for git commands
+
+```ruby
+# Wrong
+system(*%W(git branch -d -- #{branch_name}))
+
+# Correct
+system(*%W(#{Gitlab.config.git.bin_path} branch -d -- #{branch_name}))
+```
+
 ## Bypass the shell by splitting commands into separate tokens
 
 When we pass shell commands as a single string to Ruby, Ruby will let `/bin/sh` evaluate the entire string. Essentially, we are asking the shell to evaluate a one-line script. This creates a risk for shell injection attacks. It is better to split the shell command into tokens ourselves. Sometimes we use the scripting capabilities of the shell to change the working directory or set environment variables. All of this can also be achieved securely straight from Ruby
@@ -81,9 +91,9 @@ In the GitLab codebase, we avoid the option/argument ambiguity by _always_ using
 
 ```ruby
 # Wrong
-system(*%W(git branch -d #{branch_name}))
+system(*%W(#{Gitlab.config.git.bin_path} branch -d #{branch_name}))
 # Correct
-system(*%W(git branch -d -- #{branch_name}))
+system(*%W(#{Gitlab.config.git.bin_path} branch -d -- #{branch_name}))
 ```
 
 This coding style could have prevented CVE-2013-4582.
@@ -94,9 +104,9 @@ Capturing the output of shell commands with backticks reads nicely, but you are
 
 ```ruby
 # Wrong
-logs = `cd #{repo_dir} && git log`
+logs = `cd #{repo_dir} && #{Gitlab.config.git.bin_path} log`
 # Correct
-logs, exit_status = Gitlab::Popen.popen(%W(git log), repo_dir)
+logs, exit_status = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} log), repo_dir)
 
 # Wrong
 user = `whoami`
@@ -108,7 +118,7 @@ In other repositories, such as gitlab-shell you can also use `IO.popen`.
 
 ```ruby
 # Safe IO.popen example
-logs = IO.popen(%W(git log), chdir: repo_dir) { |p| p.read }
+logs = IO.popen(%W(#{Gitlab.config.git.bin_path} log), chdir: repo_dir) { |p| p.read }
 ```
 
 Note that unlike `Gitlab::Popen.popen`, `IO.popen` does not capture standard error.
diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md
index aafa2345fab1506d275beb30270f5cf1d1d757a9..86d205ba7a54a846f72ff8e913440d637f87360b 100644
--- a/doc/incoming_email/README.md
+++ b/doc/incoming_email/README.md
@@ -4,9 +4,9 @@ GitLab can be set up to allow users to comment on issues and merge requests by r
 
 ## Get a mailbox
 
-Reply by email requires an IMAP-enabled email account, with a provider or server that supports [email sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing). Sub-addressing is a feature where any email to `user+some_arbitrary_tag@example.com` will end up in the mailbox for `user@example.com`, and is supported by providers such as Gmail, Yahoo! Mail, Outlook.com and iCloud, as well as the Postfix mail server which you can run on-premises.
+Reply by email requires an IMAP-enabled email account, with a provider or server that supports [email sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing). Sub-addressing is a feature where any email to `user+some_arbitrary_tag@example.com` will end up in the mailbox for `user@example.com`, and is supported by providers such as Gmail, Google Apps, Yahoo! Mail, Outlook.com and iCloud, as well as the Postfix mail server which you can run on-premises.
 
-If you want to use Gmail with Reply by email, make sure you have [IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018) and [allow less secure apps to access the account](https://support.google.com/accounts/answer/6010255).
+If you want to use Gmail / Google Apps with Reply by email, make sure you have [IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018) and [allow less secure apps to access the account](https://support.google.com/accounts/answer/6010255).
 
 To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these instructions](./postfix.md).
 
@@ -14,30 +14,62 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
 
 ### Omnibus package installations
 
-1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `key` that references the item being replied to and fill in the details for your specific IMAP server and email account:
+1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature and fill in the details for your specific IMAP server and email account:
 
     ```ruby
-    # Postfix mail server, assumes mailbox incoming@gitlab.example.com
+    # Configuration for Postfix mail server, assumes mailbox incoming@gitlab.example.com
     gitlab_rails['incoming_email_enabled'] = true
+    
+    # The email address including a placeholder for the key that references the item being replied to.
+    # The `%{key}` placeholder is added after the user part, before the `@`.
     gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com"
-    gitlab_rails['incoming_email_host'] = "gitlab.example.com" # IMAP server host
-    gitlab_rails['incoming_email_port'] = 143 # IMAP server port
-    gitlab_rails['incoming_email_ssl'] = false # Whether the IMAP server uses SSL
-    gitlab_rails['incoming_email_email'] = "incoming"  # Email account username. Usually the full email address.
-    gitlab_rails['incoming_email_password'] = "[REDACTED]" # Email account password
-    gitlab_rails['incoming_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox".
+    
+    # Email account username
+    # With third party providers, this is usually the full email address.
+    # With self-hosted email servers, this is usually the user part of the email address.
+    gitlab_rails['incoming_email_email'] = "incoming"
+    # Email account password
+    gitlab_rails['incoming_email_password'] = "[REDACTED]"
+    
+    # IMAP server host
+    gitlab_rails['incoming_email_host'] = "gitlab.example.com"
+    # IMAP server port
+    gitlab_rails['incoming_email_port'] = 143
+    # Whether the IMAP server uses SSL
+    gitlab_rails['incoming_email_ssl'] = false
+    # Whether the IMAP server uses StartTLS
+    gitlab_rails['incoming_email_start_tls'] = false
+
+    # The mailbox where incoming mail will end up. Usually "inbox".
+    gitlab_rails['incoming_email_mailbox_name'] = "inbox"
     ```
 
     ```ruby
-    # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
+    # Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
     gitlab_rails['incoming_email_enabled'] = true
+    
+    # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+    # The `%{key}` placeholder is added after the user part, after a `+` character, before the `@`.
     gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
-    gitlab_rails['incoming_email_host'] = "imap.gmail.com" # IMAP server host
-    gitlab_rails['incoming_email_port'] = 993 # IMAP server port
-    gitlab_rails['incoming_email_ssl'] = true # Whether the IMAP server uses SSL
-    gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"  # Email account username. Usually the full email address.
-    gitlab_rails['incoming_email_password'] = "[REDACTED]" # Email account password
-    gitlab_rails['incoming_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox".
+    
+    # Email account username
+    # With third party providers, this is usually the full email address.
+    # With self-hosted email servers, this is usually the user part of the email address.
+    gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"
+    # Email account password
+    gitlab_rails['incoming_email_password'] = "[REDACTED]"
+    
+    # IMAP server host
+    gitlab_rails['incoming_email_host'] = "imap.gmail.com"
+    # IMAP server port
+    gitlab_rails['incoming_email_port'] = 993
+    # Whether the IMAP server uses SSL
+    gitlab_rails['incoming_email_ssl'] = true
+    # Whether the IMAP server uses StartTLS
+    gitlab_rails['incoming_email_start_tls'] = false
+
+    # The mailbox where incoming mail will end up. Usually "inbox".
+    gitlab_rails['incoming_email_mailbox_name'] = "inbox"
     ```
 
     As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`.
@@ -64,229 +96,146 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
     cd /home/git/gitlab
     ```
 
-1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key` that references the item being replied to:
+1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and fill in the details for your specific IMAP server and email account:
 
     ```sh
     sudo editor config/gitlab.yml
     ```
 
     ```yaml
-    # Postfix mail server, assumes mailbox incoming@gitlab.example.com
+    # Configuration for Postfix mail server, assumes mailbox incoming@gitlab.example.com
     incoming_email:
       enabled: true
+
+      # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+      # The `%{key}` placeholder is added after the user part, after a `+` character, before the `@`.
       address: "incoming+%{key}@gitlab.example.com"
+
+      # Email account username
+      # With third party providers, this is usually the full email address.
+      # With self-hosted email servers, this is usually the user part of the email address.
+      user: "incoming"
+      # Email account password
+      password: "[REDACTED]"
+
+      # IMAP server host
+      host: "gitlab.example.com"
+      # IMAP server port
+      port: 143
+      # Whether the IMAP server uses SSL
+      ssl: false
+      # Whether the IMAP server uses StartTLS
+      start_tls: false
+
+      # The mailbox where incoming mail will end up. Usually "inbox".
+      mailbox: "inbox"
     ```
 
     ```yaml
-    # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
+    # Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
     incoming_email:
       enabled: true
-      address: "gitlab-incoming+%{key}@gmail.com"
-    ```
-
-    As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`.
 
-2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`:
+      # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+      # The `%{key}` placeholder is added after the user part, after a `+` character, before the `@`.
+      address: "gitlab-incoming+%{key}@gmail.com"
 
-    ```sh
-    sudo cp config/mail_room.yml.example config/mail_room.yml
-    ```
+      # Email account username
+      # With third party providers, this is usually the full email address.
+      # With self-hosted email servers, this is usually the user part of the email address.
+      user: "gitlab-incoming@gmail.com"
+      # Email account password
+      password: "[REDACTED]"
 
-3. Uncomment the configuration options in `config/mail_room.yml` and fill in the details for your specific IMAP server and email account:
+      # IMAP server host
+      host: "imap.gmail.com"
+      # IMAP server port
+      port: 993
+      # Whether the IMAP server uses SSL
+      ssl: true
+      # Whether the IMAP server uses StartTLS
+      start_tls: false
 
-    ```sh
-    sudo editor config/mail_room.yml
-    ```
-
-    ```yaml
-    # Postfix mail server
-    :mailboxes:
-      -
-        # IMAP server host
-        :host: "gitlab.example.com"
-        # IMAP server port
-        :port: 143
-        # Whether the IMAP server uses SSL
-        :ssl: false
-        # Whether the IMAP server uses StartTLS
-        :start_tls: false
-        # Email account username. Usually the full email address.
-        :email: "incoming"
-        # Email account password
-        :password: "[REDACTED]"
-
-        # The name of the mailbox where incoming mail will end up. Usually "inbox".
-        :name: "inbox"
-
-        # Always "sidekiq".
-        :delivery_method: sidekiq
-        # Always true.
-        :delete_after_delivery: true
-        :delivery_options:
-          # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml.
-          :redis_url: redis://localhost:6379
-          # Always "resque:gitlab".
-          :namespace: resque:gitlab
-          # Always "incoming_email".
-          :queue: incoming_email
-          # Always "EmailReceiverWorker"
-          :worker: EmailReceiverWorker
-
-        # Always "redis".
-        :arbitration_method: redis
-        :arbitration_options:
-          # The URL to the Redis server. Should match the URL in config/resque.yml.
-          :redis_url: redis://localhost:6379
-          # Always "mail_room:gitlab".
-          :namespace: mail_room:gitlab
+      # The mailbox where incoming mail will end up. Usually "inbox".
+      mailbox: "inbox"
     ```
 
-    ```yaml
-    # Gmail / Google Apps
-    :mailboxes:
-      -
-        # IMAP server host
-        :host: "imap.gmail.com"
-        # IMAP server port
-        :port: 993
-        # Whether the IMAP server uses SSL
-        :ssl: true
-        # Whether the IMAP server uses StartTLS
-        :start_tls: false
-        # Email account username. Usually the full email address.
-        :email: "gitlab-incoming@gmail.com"
-        # Email account password
-        :password: "[REDACTED]"
-
-        # The name of the mailbox where incoming mail will end up. Usually "inbox".
-        :name: "inbox"
-
-        # Always "sidekiq".
-        :delivery_method: sidekiq
-        # Always true.
-        :delete_after_delivery: true
-        :delivery_options:
-          # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml.
-          :redis_url: redis://localhost:6379
-          # Always "resque:gitlab".
-          :namespace: resque:gitlab
-          # Always "incoming_email".
-          :queue: incoming_email
-          # Always "EmailReceiverWorker"
-          :worker: EmailReceiverWorker
-
-        # Always "redis".
-        :arbitration_method: redis
-        :arbitration_options:
-          # The URL to the Redis server. Should match the URL in config/resque.yml.
-          :redis_url: redis://localhost:6379
-          # Always "mail_room:gitlab".
-          :namespace: mail_room:gitlab
-    ```
+    As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`.
 
-5. Edit the init script configuration at `/etc/default/gitlab` to enable `mail_room`:
+1. Enable `mail_room` in the init script at `/etc/default/gitlab`:
 
     ```sh
     sudo mkdir -p /etc/default
     echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab
     ```
 
-6. Restart GitLab:
+1. Restart GitLab:
 
     ```sh
     sudo service gitlab restart
     ```
 
-7. Verify that everything is configured correctly:
+1. Verify that everything is configured correctly:
 
     ```sh
     sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
     ```
 
-8. Reply by email should now be working.
+1. Reply by email should now be working.
 
 ### Development
 
 1. Go to the GitLab installation directory.
 
-1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key` that references the item being replied to:
+1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and fill in the details for your specific IMAP server and email account:
 
     ```yaml
-    # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
+    # Configuration for Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
     incoming_email:
       enabled: true
+
+      # The email address including a placeholder for the key that references the item being replied to.
+      # The `%{key}` placeholder is added after the user part, before the `@`.
       address: "gitlab-incoming+%{key}@gmail.com"
-    ```
 
-    As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`.
+      # Email account username
+      # With third party providers, this is usually the full email address.
+      # With self-hosted email servers, this is usually the user part of the email address.
+      user: "gitlab-incoming@gmail.com"
+      # Email account password
+      password: "[REDACTED]"
 
-2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`:
+      # IMAP server host
+      host: "imap.gmail.com"
+      # IMAP server port
+      port: 993
+      # Whether the IMAP server uses SSL
+      ssl: true
+      # Whether the IMAP server uses StartTLS
+      start_tls: false
 
-    ```sh
-    sudo cp config/mail_room.yml.example config/mail_room.yml
+      # The mailbox where incoming mail will end up. Usually "inbox".
+      mailbox: "inbox"
     ```
 
-3. Uncomment the configuration options in `config/mail_room.yml` and fill in the details for your specific IMAP server and email account:
-
-    ```yaml
-    # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
-    :mailboxes:
-      -
-        # IMAP server host
-        :host: "imap.gmail.com"
-        # IMAP server port
-        :port: 993
-        # Whether the IMAP server uses SSL
-        :ssl: true
-        # Whether the IMAP server uses StartTLS
-        :start_tls: false
-        # Email account username. Usually the full email address.
-        :email: "gitlab-incoming@gmail.com"
-        # Email account password
-        :password: "[REDACTED]"
-
-        # The name of the mailbox where incoming mail will end up. Usually "inbox".
-        :name: "inbox"
-
-        # Always "sidekiq".
-        :delivery_method: sidekiq
-        # Always true.
-        :delete_after_delivery: true
-        :delivery_options:
-          # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml.
-          :redis_url: redis://localhost:6379
-          # Always "resque:gitlab".
-          :namespace: resque:gitlab
-          # Always "incoming_email".
-          :queue: incoming_email
-          # Always "EmailReceiverWorker"
-          :worker: EmailReceiverWorker
-
-        # Always "redis".
-        :arbitration_method: redis
-        :arbitration_options:
-          # The URL to the Redis server. Should match the URL in config/resque.yml.
-          :redis_url: redis://localhost:6379
-          # Always "mail_room:gitlab".
-          :namespace: mail_room:gitlab
-    ```
+    As mentioned, the part after `+` is ignored, and this will end up in the mailbox for `gitlab-incoming@gmail.com`.
 
-4. Uncomment the `mail_room` line in your `Procfile`:
+1. Uncomment the `mail_room` line in your `Procfile`:
 
     ```yaml
     mail_room: bundle exec mail_room -q -c config/mail_room.yml
     ```
 
-6. Restart GitLab:
+1. Restart GitLab:
 
     ```sh
     bundle exec foreman start
     ```
 
-7. Verify that everything is configured correctly:
+1. Verify that everything is configured correctly:
 
     ```sh
     bundle exec rake gitlab:incoming_email:check RAILS_ENV=development
     ```
 
-8. Reply by email should now be working.
+1. Reply by email should now be working.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 3c62b11988e240d029d631fb4fbc5bb319106746..f17477a3218a86146c86fcc215e356fa8cd09497 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -211,9 +211,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
 ### Clone the Source
 
     # Clone GitLab repository
-    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-0-stable gitlab
+    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-1-stable gitlab
 
-**Note:** You can change `8-0-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `8-1-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
 
 ### Configure It
 
@@ -253,8 +253,8 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
     nproc
 
     # Enable cluster mode if you expect to have a high load instance
-    # Ex. change amount of workers to 3 for 2GB RAM server
     # Set the number of workers to at least the number of cores
+    # Ex. change amount of workers to 3 for 2GB RAM server
     sudo -u git -H editor config/unicorn.rb
 
     # Copy the example Rack attack config
@@ -310,7 +310,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
 GitLab Shell is an SSH access and repository management software developed specially for GitLab.
 
     # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed):
-    sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.5] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production
+    sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.6] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production
 
     # By default, the gitlab-shell config is generated from your main GitLab config.
     # You can review (and modify) the gitlab-shell config as follows:
@@ -325,9 +325,14 @@ GitLab Shell is an SSH access and repository management software developed speci
     cd /home/git
     sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-git-http-server.git
     cd gitlab-git-http-server
+    sudo -u git -H git checkout 0.3.0
     sudo -u git -H make
 
 ### Initialize Database and Activate Advanced Features
+    
+    # Go to Gitlab installation folder
+
+    cd /home/git/gitlab
 
     sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
 
@@ -345,11 +350,6 @@ The `secrets.yml` file stores encryption keys for sessions and secure variables.
 Backup `secrets.yml` someplace safe, but don't store it in the same place as your database backups.
 Otherwise your secrets are exposed if one of your backups is compromised.
 
-### Install schedules
-
-    # Setup schedules
-    sudo -u gitlab_ci -H bundle exec whenever -w RAILS_ENV=production
-
 ### Install Init Script
 
 Download the init script (will be `/etc/init.d/gitlab`):
@@ -493,7 +493,7 @@ See the [omniauth integration document](../integration/omniauth.md)
 ### Build your projects
 
 GitLab can build your projects. To enable that feature you need GitLab Runners to do that for you.
-Checkout the [Gitlab Runner section](https://about.gitlab.com/gitlab-ci/#gitlab-runner) to install it
+Checkout the [GitLab Runner section](https://about.gitlab.com/gitlab-ci/#gitlab-runner) to install it
 
 ### Custom Redis Connection
 
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index aa0d03b75bcc45bd6db3f5d6f8ed17194b0e2e83..c0ccdd374586e29ae5ec53939c5fde3b968c3df0 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -109,8 +109,4 @@ On a very active server (10,000 active users) the Sidekiq process can use 1GB+ o
 - Firefox (Latest released version and [latest ESR version](https://www.mozilla.org/en-US/firefox/organizations/))
 - Safari 7+ (known problem: required fields in html5 do not work)
 - Opera (Latest released version)
-- IE 10+
-
-### Common UI problems with IE
-
-If you experience UI issues with Internet Explorer, please make sure that you have the `Compatibility View` mode disabled.
\ No newline at end of file
+- Internet Explorer (IE) 10+ but please make sure that you have the `Compatibility View` mode disabled.
\ No newline at end of file
diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md
new file mode 100644
index 0000000000000000000000000000000000000000..bc1f1673086d30f5475375a52990614bd9a48b90
--- /dev/null
+++ b/doc/integration/facebook.md
@@ -0,0 +1,97 @@
+# Facebook OAuth2 OmniAuth Provider
+
+To enable the Facebook OmniAuth provider you must register your application with Facebook. Facebook will generate an app ID and secret key for you to use.
+
+1.  Sign in to the [Facebook Developer Platform](https://developers.facebook.com/).
+
+1. Choose "My Apps" &gt; "Add a New App"
+
+1. Select the type "Website"
+
+1. Enter a name for your app. This can be anything. Consider something like "&lt;Organization&gt;'s GitLab" or "&lt;Your Name&gt;'s GitLab" or
+something else descriptive.
+
+1. Choose "Create New Facebook App ID"
+
+1. Select a Category, for example "Productivity"
+
+1. Choose "Create App ID"
+
+1. Enter the address of your GitLab installation at the bottom of the package
+
+    ![Facebook Website URL](facebook_website_url.png)
+
+1. Choose "Next"
+
+1. Choose "Skip Quick Start" in the upper right corner
+
+1. Choose "Settings" in the menu on the left
+
+1. Fill in a contact email for your app
+
+    ![Facebook App Settings](facebook_app_settings.png)
+
+1. Choose "Save Changes"
+
+1. Choose "Status & Review" in the menu on the left
+
+1. Change the switch on the right from No to Yes
+
+1. Choose "Confirm" when prompted to make the app public
+
+1. Choose "Dashboard" in the menu on the left
+
+1. Choose "Show" next to the hidden "App Secret"
+
+1. You should now see an app key and app secret (see screenshot). Keep this page open as you continue configuration.
+
+    ![Facebook API Keys](facebook_api_keys.png)
+
+1.  On your GitLab server, open the configuration file.
+
+    For omnibus package:
+
+    ```sh
+    sudo editor /etc/gitlab/gitlab.rb
+    ```
+
+    For installations from source:
+
+    ```sh
+    cd /home/git/gitlab
+
+    sudo -u git -H editor config/gitlab.yml
+    ```
+
+1.  See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
+
+1.  Add the provider configuration:
+
+    For omnibus package:
+
+    ```ruby
+    gitlab_rails['omniauth_providers'] = [
+      {
+        "name" => "facebook",
+        "app_id" => "YOUR_APP_ID",
+        "app_secret" => "YOUR_APP_SECRET"
+      }
+    ]
+    ```
+
+    For installations from source:
+
+    ```
+    - { name: 'facebook', app_id: 'YOUR_APP_ID',
+      app_secret: 'YOUR_APP_SECRET' }
+    ```
+
+1.  Change 'YOUR_APP_ID' to the API key from Facebook page in step 10.
+
+1.  Change 'YOUR_APP_SECRET' to the API secret from the Facebook page in step 10.
+
+1.  Save the configuration file.
+
+1.  Restart GitLab for the changes to take effect.
+
+On the sign in page there should now be a Facebook icon below the regular sign in form. Click the icon to begin the authentication process. Facebook will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in.
diff --git a/doc/integration/facebook_api_keys.png b/doc/integration/facebook_api_keys.png
new file mode 100644
index 0000000000000000000000000000000000000000..d6c44ac0f117feebe69adb17abc31312688a76eb
Binary files /dev/null and b/doc/integration/facebook_api_keys.png differ
diff --git a/doc/integration/facebook_app_settings.png b/doc/integration/facebook_app_settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..30dd21e198ad480931f4236ddb5f0b014efb12b4
Binary files /dev/null and b/doc/integration/facebook_app_settings.png differ
diff --git a/doc/integration/facebook_website_url.png b/doc/integration/facebook_website_url.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc3088bb2faa68b6555f83faee04b04ecc762bf7
Binary files /dev/null and b/doc/integration/facebook_website_url.png differ
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index c5cecbc2f2dc50e138ad7d15dab0b1b82f93b39d..bd9550c6ddb12cdefc688f9471763d4faf0862ce 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -73,8 +73,9 @@ Now we can choose one or more of the Supported Providers below to continue confi
 - [Bitbucket](bitbucket.md)
 - [GitLab.com](gitlab.md)
 - [Google](google.md)
-- [Shibboleth](shibboleth.md)
+- [Facebook](facebook.md)
 - [Twitter](twitter.md)
+- [Shibboleth](shibboleth.md)
 - [SAML](saml.md)
 - [Crowd](crowd.md)
 
diff --git a/doc/operations/unicorn.md b/doc/operations/unicorn.md
index 31b432cd411df24d2308d085d4ef6873bbf05e6a..3998da01f015ea00437ebbbbecb5192127e20c50 100644
--- a/doc/operations/unicorn.md
+++ b/doc/operations/unicorn.md
@@ -78,7 +78,7 @@ threshold is a random value between 200 and 250 MB.  The master process (PID
 ```
 
 One other thing that stands out in the log snippet above, taken from
-Gitlab.com, is that 'worker 4' was serving requests for only 23 seconds. This
+GitLab.com, is that 'worker 4' was serving requests for only 23 seconds. This
 is a normal value for our current GitLab.com setup and traffic.
 
 The high frequency of Unicorn memory restarts on some GitLab sites can be a
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index 7a6a1958445ae4afe8c4e25573710defebbf382e..8d4c2ceab7d25c4a25623010d87ab5487bff40b1 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -15,8 +15,8 @@ documentation](doc/workflow/add-user/add-user.md).
 |---------------------------------------|---------|------------|-------------|----------|--------|
 | Create new issue                      | ✓       | ✓          | ✓           | ✓        | ✓      |
 | Leave comments                        | ✓       | ✓          | ✓           | ✓        | ✓      |
-| Pull project code                     |         | ✓          | ✓           | ✓        | ✓      |
-| Download project                      |         | ✓          | ✓           | ✓        | ✓      |
+| Pull project code                     | ✓        | ✓          | ✓           | ✓        | ✓      |
+| Download project                      | ✓        | ✓          | ✓           | ✓        | ✓      |
 | Create code snippets                  |         | ✓          | ✓           | ✓        | ✓      |
 | Manage issue tracker                  |         | ✓          | ✓           | ✓        | ✓      |
 | Manage labels                         |         | ✓          | ✓           | ✓        | ✓      |
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index db3f6bb40bd8a4fbf68274a458ac72b54e80e696..606532a6fbe91892c4dac7d4e0f3ae826783ce7f 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -16,7 +16,7 @@ You need to keep a separate copy of `/etc/gitlab/gitlab-secrets.json`
 from source). This file contains the database encryption key used
 for two-factor authentication. If you restore a GitLab backup without
 restoring the database encryption key, users who have two-factor
-authentication enabled will loose access to your GitLab server.
+authentication enabled will lose access to your GitLab server.
 
 If you are interested in GitLab CI backup please follow to the [CI backup documentation](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/raketasks/backup_restore.md)*
 
@@ -29,7 +29,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
 ```
 
 Also you can choose what should be backed up by adding environment variable SKIP. Available options: db,
-uploads (attachments), repositories. Use a comma to specify several options at the same time.
+uploads (attachments), repositories, builds(CI build output logs). Use a comma to specify several options at the same time.
 
 ```
 sudo gitlab-rake gitlab:backup:create SKIP=db,uploads
diff --git a/doc/release/monthly.md b/doc/release/monthly.md
index bd8a67d1d85a81f438db55db5cbb6c0449db1bf9..4925816daaa7be296aca4ab001893b0802a47711 100644
--- a/doc/release/monthly.md
+++ b/doc/release/monthly.md
@@ -25,68 +25,84 @@ If the release is falling behind immediately warn the team.
 
 ## Create an overall issue and follow it
 
-Create issue for GitLab CE project(internal). Name it "Release x.x.x" for easier searching.
-Replace the dates with actual dates based on the number of workdays before the release.
-All steps from issue template are explained below
+Create an issue in the GitLab CE project. Name it "Release x.x" and tag it with
+the `release` label for easier searching. Replace the dates with actual dates
+based on the number of workdays before the release. All steps from issue
+template are explained below:
 
 ```
-Xth: (7 working days before the 22nd)
+### Xth: (7 working days before the 22nd)
 
-- [ ] Triage the omnibus-gitlab milestone
+- [ ] Triage the [Omnibus milestone]
 
-Xth: (6 working days before the 22nd)
+### Xth: (6 working days before the 22nd)
 
-- [ ] Merge CE master in to EE master via merge request (#LINK)
+- [ ] Merge CE `master` into EE `master` via merge request (#LINK)
 - [ ] Determine QA person and notify this person
 - [ ] Check the tasks in [how to rc1 guide](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/release/howto_rc1.md) and delegate tasks if necessary
-- [ ] Create CE, EE, CI RC1 versions (#LINK)
-- [ ] Build RC1 packages (EE first) (#LINK)
+- [ ] Create CE and EE RC1 versions (#LINK)
+- [ ] Build RC1 packages
 
-Xth: (5 working days before the 22nd)
+### Xth: (5 working days before the 22nd)
 
 - [ ] Do QA and fix anything coming out of it (#LINK)
-- [ ] Close the omnibus-gitlab milestone
-- [ ] Prepare the blog post (#LINK)
+- [ ] Close the [Omnibus milestone]
+- [ ] Prepare the [blog post]
 
-Xth: (4 working days before the 22nd)
+### Xth: (4 working days before the 22nd)
 
-- [ ] Update GitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package)
-- [ ] Update ci.gitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package)
-- [ ] Create regression issues (CE, CI) (#LINK)
-- [ ] Tweet about rc1 (#LINK), proposed text: 
+- [ ] Update GitLab.com with RC1
+- [ ] Create the regression issue in the CE issue tracker:
 
-> GitLab x.x.0.rc1 is available https://packages.gitlab.com/gitlab/unstable Use at your own risk. Please link regressions issues from LINK_TO_REGRESSION_ISSUE
+    > This is a meta issue to index possible regressions in this monthly release
+    > and any patch versions.
+    >
+    > Please do not raise or discuss issues directly in this issue but link to
+    > issues that might warrant a patch release. If there is a Merge Request
+    > that fixes the issue, please link to that as well.
+    >
+    > Please only post one regression issue and/or merge request per comment.
+    > Comments will be updated by the release manager as they are addressed.
 
-Xth: (3 working days before the 22nd)
+- [ ] Tweet about RC1 release:
 
-- [ ] Merge CE stable branch into EE stable branch
+    > GitLab x.y.0.rc1 is available: https://packages.gitlab.com/gitlab/unstable
+    > Use at your own risk. Please link regressions issues from
+    > LINK_TO_REGRESSION_ISSUE
 
-Xth: (2 working days before the 22nd)
+### Xth: (3 working days before the 22nd)
 
-- [ ] Check that everyone is mentioned on the blog post using `@all` (the reviewer should have done this one working day ago)
-- [ ] Check that MVP is added to the mvp page (source/mvp/index.html in www-gitlab-com)
+- [ ] Merge `x-y-stable` into `x-y-stable-ee`
+- [ ] Check that everyone is mentioned on the [blog post] using `@all`
 
-Xth: (1 working day before the 22nd)
+### Xth: (2 working days before the 22nd)
 
-- [ ] Merge CE stable into EE stable
-- [ ] Create CE, EE, CI release candidates (#LINK) (hopefully final ones with the same commit as the release tomorrow) 
+- [ ] Check that MVP is added to the [MVP page]
+
+### Xth: (1 working day before the 22nd)
+
+- [ ] Merge `x-y-stable` into `x-y-stable-ee`
+- [ ] Create CE and EE release candidates
 - [ ] Create Omnibus tags and build packages for the latest release candidates
-- [ ] Update GitLab.com with the latest RC (#LINK)
-- [ ] Update ci.gitLab.com with the latest RC (#LINK)
+- [ ] Update GitLab.com with the latest RC
 
-22nd before 1200 CET:
+### 22nd before 1200 CET:
 
 Release before 1200 CET / 2AM PST, to make sure the majority of our users
 get the new version on the 22nd and there is sufficient time in the European
 workday to quickly fix any issues.
 
-- [ ] Merge CE stable into EE stable (#LINK)
-- [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools) (#LINK)
+- [ ] Merge `x-y-stable` into `x-y-stable-ee`
+- [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools)
 - [ ] Create the 'x.y.0' version on version.gitlab.com
-- [ ] Try to do before 1100 CET: Create and push omnibus tags for x.y.0 (will auto-release the packages) (#LINK)
-- [ ] Try to do before 1200 CET: Publish the release blog post (#LINK)
-- [ ] Tweet about the release (blog post) (#LINK)
-- [ ] Schedule a second tweet of the release announcement with the same text at 1800 CET / 8AM PST
+- [ ] Try to do before 1100 CET: Create and push Omnibus tags for x.y.0 (will auto-release the packages)
+- [ ] Try to do before 1200 CET: Publish the release [blog post]
+- [ ] Tweet about the release
+- [ ] Schedule a second Tweet of the release announcement with the same text at 1800 CET / 8AM PST
+
+[Omnibus milestone]: LINK_TO_OMNIBUS_MILESTONE
+[blog post]: LINK_TO_WIP_BLOG_POST
+[MVP page]: https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/source/mvp/index.html
 ```
 
 - - -
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index b6b8000af4e75b93603ec38948e8e31f76e26f04..0bdb4070e745e031116b5d3400ec011cb8717ead 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -15,8 +15,7 @@ Note: It is a best practice to use a password for an SSH key, but it is not
 required and you can skip creating a password by pressing enter. Note that
 the password you choose here can't be altered or retrieved.
 
-To generate a new SSH key, use the following command:
-```bash
+To generate a new SSH key, use the following commandGitLab```bash
 ssh-keygen -t rsa -C "$your_email"
 ```
 This command will prompt you for a location and filename to store the key
@@ -82,7 +81,7 @@ How to add your ssh key to Eclipse: http://wiki.eclipse.org/EGit/User_Guide#Ecli
 
 ## Tip: Non-default OpenSSH key file names or locations
 
-If, for whatever reason, you decide to specify a non-default location and filename for your Gitlab SSH key pair, you must configure your SSH client to find your Gitlab SSH private key for connections to your Gitlab server (perhaps gitlab.com). For OpenSSH clients, this is handled in the `~/.ssh/config` file with a stanza similar to the following:
+If, for whatever reason, you decide to specify a non-default location and filename for your GitLab SSH key pair, you must configure your SSH client to find your GitLab SSH private key for connections to your GitLab server (perhaps gitlab.com). For OpenSSH clients, this is handled in the `~/.ssh/config` file with a stanza similar to the following:
 
 ```
 #
@@ -97,7 +96,7 @@ User mygitlabusername
 Another example
 ```
 #
-# Our company's internal Gitlab server
+# Our company's internal GitLab server
 #
 Host my-gitlab.company.com
 RSAAuthentication yes
diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md
index 7ad4935e83979a4f11858154d43ca27ece0e6b8f..305017b704816cea41a22b80e18efbd1d355ae7a 100644
--- a/doc/update/7.14-to-8.0.md
+++ b/doc/update/7.14-to-8.0.md
@@ -84,6 +84,7 @@ Now we download `gitlab-git-http-server` and install it in `/home/git/gitlab-git
 cd /home/git
 sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-git-http-server.git
 cd gitlab-git-http-server
+sudo -u git -H git checkout 0.2.14
 sudo -u git -H make
 ```
 
diff --git a/doc/update/8.0-to-8.1.md b/doc/update/8.0-to-8.1.md
new file mode 100644
index 0000000000000000000000000000000000000000..d57c0d0674d44cdfbc3ce68fffd57b803816009d
--- /dev/null
+++ b/doc/update/8.0-to-8.1.md
@@ -0,0 +1,171 @@
+# From 8.0 to 8.1
+
+**NOTE:** GitLab 8.0 introduced several significant changes related to
+installation and configuration which *are not duplicated here*. Be sure you're
+already running a working version of 8.0 before proceeding with this guide.
+
+### 0. Double-check your Git version
+
+**This notice applies only to /usr/local/bin/git**
+
+If you compiled Git from source on your GitLab server then please double-check
+that you are using a version that protects against CVE-2014-9390. For six
+months after this vulnerability became known the GitLab installation guide
+still contained instructions that would install the outdated, 'vulnerable' Git
+version 2.1.2.
+
+Run the following command to get your current Git version:
+
+```sh
+/usr/local/bin/git --version
+```
+
+If you see 'No such file or directory' then you did not install Git according
+to the outdated instructions from the GitLab installation guide and you can go
+to the next step 'Stop server' below.
+
+If you see a version string then it should be v1.8.5.6, v1.9.5, v2.0.5, v2.1.4,
+v2.2.1 or newer. You can use the [instructions in the GitLab source
+installation
+guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
+to install a newer version of Git.
+
+### 1. Stop server
+
+    sudo service gitlab stop
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 8-1-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-1-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v2.6.5
+```
+
+### 5. Update gitlab-git-http-server
+
+```bash
+cd /home/git/gitlab-git-http-server
+sudo -u git -H git fetch origin
+sudo -u git -H git checkout 0.3.0
+sudo -u git -H make
+```
+
+### 6. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+# Update init.d script
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+### 7. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+git diff origin/8-0-stable:config/gitlab.yml.example origin/8-1-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+View changes between the previous recommended Nginx configuration and the
+current one:
+
+```sh
+# For HTTPS configurations
+git diff origin/8-0-stable:lib/support/nginx/gitlab-ssl origin/8-1-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/8-0-stable:lib/support/nginx/gitlab origin/8-1-stable:lib/support/nginx/gitlab
+```
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-git-http-server listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-1-stable/lib/support/init.d/gitlab.default.example#L34
+
+### 8. Start application
+
+    sudo service gitlab start
+    sudo service nginx restart
+
+### 9. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+    sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check:
+
+    sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.0)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 7.14 to 8.0](7.14-to-8.0.md), except for the database migration
+(The backup is already migrated to the previous version)
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+## Troubleshooting
+
+### "You appear to have cloned an empty repository."
+
+See the [7.14 to 8.0 update guide](7.14-to-8.0.md#troubleshooting).
diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md
new file mode 100644
index 0000000000000000000000000000000000000000..3772f624e9860ee4df7b26627106b6657b2504fd
--- /dev/null
+++ b/doc/update/8.1-to-8.2.md
@@ -0,0 +1,188 @@
+# From 8.1 to 8.2
+
+**NOTE:** GitLab 8.0 introduced several significant changes related to
+installation and configuration which *are not duplicated here*. Be sure you're
+already running a working version of 8.0 before proceeding with this guide.
+
+### 0. Double-check your Git version
+
+**This notice applies only to /usr/local/bin/git**
+
+If you compiled Git from source on your GitLab server then please double-check
+that you are using a version that protects against CVE-2014-9390. For six
+months after this vulnerability became known the GitLab installation guide
+still contained instructions that would install the outdated, 'vulnerable' Git
+version 2.1.2.
+
+Run the following command to get your current Git version:
+
+```sh
+/usr/local/bin/git --version
+```
+
+If you see 'No such file or directory' then you did not install Git according
+to the outdated instructions from the GitLab installation guide and you can go
+to the next step 'Stop server' below.
+
+If you see a version string then it should be v1.8.5.6, v1.9.5, v2.0.5, v2.1.4,
+v2.2.1 or newer. You can use the [instructions in the GitLab source
+installation
+guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
+to install a newer version of Git.
+
+### 1. Stop server
+
+    sudo service gitlab stop
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 8-2-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-2-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch
+sudo -u git -H git checkout v2.6.5
+```
+
+### 5. Replace gitlab-git-http-server with gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires [Go
+1.5](https://golang.org/dl) which should already be on your system
+from GitLab 8.1.
+
+```bash
+cd /home/git
+sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
+cd gitlab-workhorse
+sudo -u git -H git checkout 0.3.1
+sudo -u git -H make
+```
+
+Update the GitLab init script and 'default' file.
+
+```
+cd /home/git/gitlab
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+test -e /etc/default/gitlab && \
+  sudo sed -i.pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /etc/default/gitlab
+```
+
+Make sure that you also update your **NGINX configuration** to use
+the new gitlab-workhorse.socket file.
+
+### 6. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+# Update init.d script
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+### 7. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+git diff origin/8-1-stable:config/gitlab.yml.example origin/8-2-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+View changes between the previous recommended Nginx configuration and the
+current one:
+
+```sh
+# For HTTPS configurations
+git diff origin/8-1-stable:lib/support/nginx/gitlab-ssl origin/8-2-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/8-1-stable:lib/support/nginx/gitlab origin/8-2-stable:lib/support/nginx/gitlab
+```
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-git-http-server listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-2-stable/lib/support/init.d/gitlab.default.example#L34
+
+### 8. Start application
+
+    sudo service gitlab start
+    sudo service nginx restart
+
+### 9. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+    sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check:
+
+    sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.0)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 7.14 to 8.0](7.14-to-8.0.md), except for the database migration
+(The backup is already migrated to the previous version)
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+## Troubleshooting
+
+### "You appear to have cloned an empty repository."
+
+See the [7.14 to 8.0 update guide](7.14-to-8.0.md#troubleshooting).
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index a596ea3845624f1fd806402af9b8182d7ce46b4f..a7de5648c0ea51d72f5580a498dc9a50de9fb18e 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -60,6 +60,9 @@ sudo -u git -H python mysql-postgresql-converter/db_converter.py gitlabhq_produc
 sudo -u git -H ed -s db/database.sql < mysql-postgresql-converter/move_drop_indexes.ed
 
 # Compress database backup
+# Warning: If you have Gitlab 7.12.0 or older skip this step and import the database.sql directly into the backup with:
+# sudo -u git -H tar rf TIMESTAMP_gitlab_backup.tar db/database.sql
+# The compressed databasedump is not supported at 7.12.0 and older.
 sudo -u git -H gzip db/database.sql
 
 # Replace the MySQL dump in TIMESTAMP_gitlab_backup.tar.
@@ -71,4 +74,5 @@ sudo -u git -H tar rf TIMESTAMP_gitlab_backup.tar db/database.sql.gz
 
 # Done! TIMESTAMP_gitlab_backup.tar can now be restored into a Postgres GitLab
 # installation.
+# See https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/raketasks/backup_restore.md for more information about backups.
 ```
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index da719229ab6854dabac1dfce7deff1fa39b1e3d9..593722eb01ff43e7af394446ca7f52827441742c 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -49,9 +49,7 @@ sudo -u git -H bundle install --without development test mysql --deployment
 sudo -u git -H bundle install --without development test postgres --deployment
 
 sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
-sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
-sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
-sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
 ```
 
 ### 5. Start application
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index ef99a69f60a81bb21e5553cc7bee73cce5ab092a..7d838187a265fd7b2889e76409105ea70440ae66 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -69,7 +69,10 @@ X-Gitlab-Event: Push Hook
       }
     }
   ],
-  "total_commits_count": 4
+  "total_commits_count": 4,
+  "added": ["CHANGELOG"],
+  "modified": ["app/controller/application.rb"],
+  "removed": []
 }
 ```
 
diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md
index f608674faf609ab8c9b5fc87b12bbb746b8e40be..9a24a1e252acef81ecc3213d23c60517e83b0969 100644
--- a/doc/workflow/gitlab_flow.md
+++ b/doc/workflow/gitlab_flow.md
@@ -26,7 +26,7 @@ After getting used to these three steps the branching model becomes the challeng
 Since many organizations new to git have no conventions how to work with it, it can quickly become a mess.
 The biggest problem they run into is that many long running branches that each contain part of the changes are around.
 People have a hard time figuring out which branch they should develop on or deploy to production.
-Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html)
+Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html).
 We think there is still room for improvement and will detail a set of practices we call GitLab flow.
 
 ## Git flow and its problems
diff --git a/doc/workflow/importing/import_projects_from_gitlab_com.md b/doc/workflow/importing/import_projects_from_gitlab_com.md
index f4c4e955d46982c9d54291deb9c3902ba4ff6c4a..1117db98e7e40d18ad0f7d6e3fb349bafa55df51 100644
--- a/doc/workflow/importing/import_projects_from_gitlab_com.md
+++ b/doc/workflow/importing/import_projects_from_gitlab_com.md
@@ -2,12 +2,12 @@
 
 You can import your existing GitLab.com projects to your GitLab instance. But keep in mind that it is possible only if
 GitLab support is enabled on your GitLab instance. 
-You can read more about Gitlab support [here](http://doc.gitlab.com/ce/integration/gitlab.html)
+You can read more about GitLab support [here](http://doc.gitlab.com/ce/integration/gitlab.html)
 To get to the importer page you need to go to "New project" page.
 
 ![New project page](gitlab_importer/new_project_page.png)
 
-Click on the "Import projects from Gitlab.com" link and you will be redirected to GitLab.com 
+Click on the "Import projects from GitLab.com" link and you will be redirected to GitLab.com
 for permission to access your projects. After accepting, you'll be automatically redirected to the importer.
 
 
diff --git a/doc_styleguide.md b/doc_styleguide.md
index 656bb1d17ff244be0bf421deb537197bb37e8f17..cceb449a8543ac1155a61afaed5b0a0405645937 100644
--- a/doc_styleguide.md
+++ b/doc_styleguide.md
@@ -15,6 +15,8 @@ For subtitles, use '##', '###' and so on.
 - Do not duplicate information.
 - Be brief and clear.
 - Whenever it applies, add documents in alphabetical order.
+- Write in US English
+- Use [single spaces](http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html) instead of double spaces.
 
 ## Images
 
diff --git a/features/profile/profile.feature b/features/profile/profile.feature
index 27c0bde364e989f9267c27dbfd528e4abd556331..168d9d30b508d23017d4edc6682086870fdd9da1 100644
--- a/features/profile/profile.feature
+++ b/features/profile/profile.feature
@@ -7,6 +7,7 @@ Feature: Profile
     Given I visit profile page
     Then I should see my profile info
 
+  @javascript
   Scenario: I can see groups I belong to
     Given I have group with projects
     When I visit profile page
diff --git a/features/project/commits/tags.feature b/features/project/commits/tags.feature
index 02f399f7cad9038ee4ddc900c2d0261a21d4059c..56ee091acc0399cf07220a17251b965014e652bb 100644
--- a/features/project/commits/tags.feature
+++ b/features/project/commits/tags.feature
@@ -12,6 +12,12 @@ Feature: Project Commits Tags
     And I submit new tag form
     Then I should see new tag created
 
+  Scenario: I create a tag with release notes
+    Given I click new tag link
+    And I submit new tag form with release notes
+    Then I should see new tag created
+    And I should see tag release notes
+
   Scenario: I create a tag with invalid name
     And I click new tag link
     And I submit new tag form with invalid name
@@ -27,15 +33,13 @@ Feature: Project Commits Tags
     And I submit new tag form with tag that already exists
     Then I should see new an error that tag already exists
 
-  @javascript
   Scenario: I delete a tag
+    Given I visit tag 'v1.1.0' page
     Given I delete tag 'v1.1.0'
     Then I should not see tag 'v1.1.0'
 
-  @javascript
-  Scenario: I delete all tags and see info message
-    Given I delete all tags
-    Then I should see tags info message
-
-  # @wip
-  # Scenario: I can download project by tag
+  Scenario: I add release notes to the tag
+    Given I visit tag 'v1.1.0' page
+    When I click edit tag link
+    And I fill release notes and submit form
+    Then I should see tag release notes
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 83055188bac82d399a9b048840c2c8b71119e8f7..6cd081c868e6582c66be1a40de7a032c1def9b4b 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -10,6 +10,21 @@ Feature: Project Merge Requests
     Then I should see "Bug NS-04" in merge requests
     And I should not see "Feature NS-03" in merge requests
 
+  Scenario: I should see CI status for merge requests
+    Given project "Shop" have "Bug NS-05" open merge request with diffs inside
+    Given "Bug NS-05" has CI status
+    When I visit project "Shop" merge requests page
+    Then I should see merge request "Bug NS-05" with CI status
+
+  Scenario: I should not see target branch name when it is project's default branch
+    Then I should see "Bug NS-04" in merge requests
+    And I should not see "master" branch
+
+  Scenario: I should see target branch when it is different from default
+    Given project "Shop" have "Bug NS-06" open merge request
+    When I visit project "Shop" merge requests page
+    Then I should see "other_branch" branch
+
   Scenario: I should see rejected merge requests
     Given I click link "Closed"
     Then I should see "Feature NS-03" in merge requests
diff --git a/features/project/project.feature b/features/project/project.feature
index b3fb0794547e4773ee6c57ccccfaf05cfde247c3..1a53945eb04f562a7890d316b33a265a2bf96045 100644
--- a/features/project/project.feature
+++ b/features/project/project.feature
@@ -31,6 +31,12 @@ Feature: Project
     And I visit project "Shop" page
     Then I should see project "Shop" README
 
+  Scenario: I should see last commit with CI
+    Given project "Shop" has CI enabled
+    Given project "Shop" has CI build
+    And I visit project "Shop" page
+    And I should see last commit with CI status
+
   @javascript
   Scenario: I should see project activity
     When I visit project "Shop" activity page
diff --git a/features/project/snippets.feature b/features/project/snippets.feature
index 77e42a1a38b93bc146942348a9b24e33d730713a..270557cbde7adfb8e8a250308bf6c6dd1a600ed7 100644
--- a/features/project/snippets.feature
+++ b/features/project/snippets.feature
@@ -30,5 +30,5 @@ Feature: Project Snippets
 
   Scenario: I destroy "Snippet one"
     Given I visit snippet page "Snippet one"
-    And I click link "Remove Snippet"
+    And I click link "Delete"
     Then I should not see "Snippet one" in snippets
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index 377c5e1a9a7d6390f2e78e9a42df490c1ba8eb10..69aa79f2d2462fae3a494ba539f14aa0601e88d5 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -90,6 +90,16 @@ Feature: Project Source Browse Files
     Then I am on the new file page
     And I see a commit error message
 
+  @javascript
+  Scenario: I can create file with a directory name
+    Given I click on "New file" link in repo
+    And I fill the new file name with a new directory
+    And I edit code
+    And I fill the commit message
+    And I click on "Commit changes"
+    Then I am redirected to the new file with directory
+    And I should see its new content
+
   @javascript
   Scenario: I can edit file
     Given I click on ".gitignore" file in repo
@@ -205,3 +215,9 @@ Feature: Project Source Browse Files
     And I see the ref 'test' has been selected
     And I visit the 'test' tree
     Then I see the commit data
+
+  @javascript
+  Scenario: I browse code with a leading dot in the directory
+    Given I switch ref to fix
+    And I visit the fix tree
+    Then I see the commit data for a directory with a leading dot
diff --git a/features/snippets/snippets.feature b/features/snippets/snippets.feature
index 4f617b6bed820cf16922048381e14d1ffb302144..e15d7c79342e7d38d74f7b758ba02e5d629a4586 100644
--- a/features/snippets/snippets.feature
+++ b/features/snippets/snippets.feature
@@ -24,7 +24,7 @@ Feature: Snippets
 
   Scenario: I destroy "Personal snippet one"
     Given I visit snippet page "Personal snippet one"
-    And I click link "Destroy"
+    And I click link "Delete"
     Then I should not see "Personal snippet one" in snippets
 
   Scenario: I create new internal snippet
diff --git a/features/steps/abuse_reports.rb b/features/steps/abuse_reports.rb
index 56652ff6f057ea09c4922a6db771229e9dc01846..499accb0b080a5d7499a6988c05e535ba6afea16 100644
--- a/features/steps/abuse_reports.rb
+++ b/features/steps/abuse_reports.rb
@@ -23,7 +23,7 @@ class Spinach::Features::AbuseReports < Spinach::FeatureSteps
   end
 
   step 'I should see a red "Report abuse" button' do
-    expect(find(:css, '.report_abuse')).to have_selector(:css, 'span.btn-close')
+    expect(page).to have_button("Already reported for abuse")
   end
 
   def user_mike
diff --git a/features/steps/admin/projects.rb b/features/steps/admin/projects.rb
index 17233f89f38adb2fc2332c1b11e801a4216eca67..5a1cc9aa15145c6f190efef66e92cd6d617f1112 100644
--- a/features/steps/admin/projects.rb
+++ b/features/steps/admin/projects.rb
@@ -41,6 +41,8 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
   end
 
   step 'I transfer project to group \'Web\'' do
+    allow_any_instance_of(Projects::TransferService).
+      to receive(:move_uploads_to_new_namespace).and_return(true)
     find(:xpath, "//input[@id='new_namespace_id']").set group.id
     click_button 'Transfer'
   end
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 69ddfa42c062df0d9f9b3d49b0c6bdd889d6ec46..70388c18fcf637e31861d7919a79e9bfdcfc5053 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -6,7 +6,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
   include Select2Helper
 
   step 'I should see back to dashboard button' do
-    expect(page).to have_content 'Back to dashboard'
+    expect(page).to have_content 'Go to dashboard'
   end
 
   step 'gitlab user "Mike"' do
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 8cf24705a5ecc15d5afc8b8a240ad4d11a99ed0a..40b2aa7c357761bde578679b678d1bf39a1cb6ae 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -59,7 +59,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
   step 'I should not see the "Remove avatar" button' do
     expect(page).not_to have_link("Remove avatar")
   end
-  
+
   step 'I should see the gravatar host link' do
     expect(page).to have_link("gravatar.com")
   end
@@ -159,10 +159,9 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
   end
 
   step 'I should see my user page' do
-    expect(page).to have_content "User Activity"
-
-    page.within '.navbar-gitlab' do
+    page.within ".cover-block" do
       expect(page).to have_content current_user.name
+      expect(page).to have_content current_user.username
     end
   end
 
@@ -176,7 +175,13 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
   end
 
   step 'I should see groups I belong to' do
-    expect(page).to have_css('.profile-groups-avatars', visible: true)
+    page.within ".content" do
+      click_link "Groups"
+    end
+
+    page.within "#groups" do
+      expect(page).to have_content @group.name
+    end
   end
 
   step 'I click on new application button' do
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index a3cb83880e30b00de8c88219a71d77632698d981..e5b3f27135da84d9ec21e8b0dc0cf23a7d032904 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -113,7 +113,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
   end
 
   step 'I click status link' do
-    click_link "Builds"
+    find('.commit-ci-menu').click_link "Builds"
   end
 
   step 'I see builds list' do
diff --git a/features/steps/project/commits/tags.rb b/features/steps/project/commits/tags.rb
index e6f8faf50fdec75a764adaca1e26317ba9e8cec4..eff4234a44a2487dfd8c718508eca529dfb6bbbc 100644
--- a/features/steps/project/commits/tags.rb
+++ b/features/steps/project/commits/tags.rb
@@ -18,6 +18,18 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps
     click_button 'Create tag'
   end
 
+  step 'I submit new tag form with release notes' do
+    fill_in 'tag_name', with: 'v7.0'
+    fill_in 'ref', with: 'master'
+    fill_in 'release_description', with: 'Awesome release notes'
+    click_button 'Create tag'
+  end
+
+  step 'I fill release notes and submit form' do
+    fill_in 'release_description', with: 'Awesome release notes'
+    click_button 'Save changes'
+  end
+
   step 'I submit new tag form with invalid name' do
     fill_in 'tag_name', with: 'v 1.0'
     fill_in 'ref', with: 'master'
@@ -52,31 +64,27 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps
     expect(page).to have_content 'Tag already exists'
   end
 
+  step "I visit tag 'v1.1.0' page" do
+    click_link 'v1.1.0'
+  end
+
   step "I delete tag 'v1.1.0'" do
-    page.within '.tags' do
+    page.within('.content') do
       first('.btn-remove').click
-      sleep 0.05
     end
   end
 
   step "I should not see tag 'v1.1.0'" do
     page.within '.tags' do
-      expect(page.all(visible: true)).not_to have_content 'v1.1.0'
+      expect(page).not_to have_link 'v1.1.0'
     end
   end
 
-  step 'I delete all tags' do
-    page.within '.tags' do
-      page.all('.btn-remove').each do |remove|
-        remove.click
-        sleep 0.05
-      end
-    end
+  step 'I click edit tag link' do
+    click_link 'Edit release notes'
   end
 
-  step 'I should see tags info message' do
-    page.within '.tags' do
-      expect(page).to have_content 'Repository has no tags yet.'
-    end
+  step 'I should see tag release notes' do
+    expect(page).to have_content 'Awesome release notes'
   end
 end
diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb
index 4abd5288d51de108d9467bafed76fa1ddb2de36f..98f31f3b76a194d854087e70264c45910dc5f24f 100644
--- a/features/steps/project/graph.rb
+++ b/features/steps/project/graph.rb
@@ -25,9 +25,9 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
 
   step 'page should have CI graphs' do
     expect(page).to have_content 'Overall'
-    expect(page).to have_content 'Builds chart for last week'
-    expect(page).to have_content 'Builds chart for last month'
-    expect(page).to have_content 'Builds chart for last year'
+    expect(page).to have_content 'Builds for last week'
+    expect(page).to have_content 'Builds for last month'
+    expect(page).to have_content 'Builds for last year'
     expect(page).to have_content 'Commit duration in minutes for last 30 commits'
   end
 
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 875bf6c4676096455410eed1cab449c52436671f..d5f2c4209a12376b4727287621f3ee35758d36ff 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -40,6 +40,14 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
     expect(page).to have_content "Bug NS-04"
   end
 
+  step 'I should not see "master" branch' do
+    expect(page).not_to have_content "master"
+  end
+
+  step 'I should see "other_branch" branch' do
+    expect(page).to have_content "other_branch"
+  end
+
   step 'I should see "Bug NS-04" in merge requests' do
     expect(page).to have_content "Bug NS-04"
   end
@@ -93,6 +101,18 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
           )
   end
 
+  step 'project "Shop" have "Bug NS-06" open merge request' do
+    create(:merge_request,
+           title: "Bug NS-06",
+           source_project: project,
+           target_project: project,
+           source_branch: 'fix',
+           target_branch: 'other_branch',
+           author: project.users.first,
+           description: "# Description header"
+          )
+  end
+
   step 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do
     create(:merge_request_with_diffs,
            title: "Bug NS-05",
@@ -338,6 +358,19 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
     expect(page).to have_content('diff --git')
   end
 
+  step '"Bug NS-05" has CI status' do
+    project = merge_request.source_project
+    project.enable_ci
+    ci_commit = create :ci_commit, gl_project: project, sha: merge_request.last_commit.id
+    create :ci_build, commit: ci_commit
+  end
+
+  step 'I should see merge request "Bug NS-05" with CI status' do
+    page.within ".mr-list" do
+      expect(page).to have_link "Build status: pending"
+    end
+  end
+
   def merge_request
     @merge_request ||= MergeRequest.find_by!(title: "Bug NS-05")
   end
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index 15f77734cb2053014828b638797aa7a3cf40286d..9ca7c8ebbc7ef8ed8efcbeb3f7f137d14e16c5eb 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -86,13 +86,13 @@ class Spinach::Features::Project < Spinach::FeatureSteps
   end
 
   step 'I should see project "Forum" README' do
-    page.within('#README') do
+    page.within('.readme-holder') do
       expect(page).to have_content 'Sample repo for testing gitlab features'
     end
   end
 
   step 'I should see project "Shop" README' do
-    page.within('#README') do
+    page.within('.readme-holder') do
       expect(page).to have_content 'testme'
     end
   end
@@ -124,11 +124,11 @@ class Spinach::Features::Project < Spinach::FeatureSteps
   end
 
   step 'I should see back to dashboard button' do
-    expect(page).to have_content 'Back to dashboard'
+    expect(page).to have_content 'Go to dashboard'
   end
 
   step 'I should see back to group button' do
-    expect(page).to have_content 'Back to group'
+    expect(page).to have_content 'Go to group'
   end
 
   step 'I click notifications drop down button' do
diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb
index db8ad08bb9e9c098bc012c21b653ec8aaa601419..a3aef9bf8c30c417093bb70d34489fd6b05a93fd 100644
--- a/features/steps/project/snippets.rb
+++ b/features/steps/project/snippets.rb
@@ -22,7 +22,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
   end
 
   step 'I click link "New Snippet"' do
-    click_link "Add new snippet"
+    click_link "New Snippet"
   end
 
   step 'I click link "Snippet one"' do
@@ -42,13 +42,13 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
   end
 
   step 'I click link "Edit"' do
-    page.within ".file-title" do
+    page.within ".page-title" do
       click_link "Edit"
     end
   end
 
-  step 'I click link "Remove Snippet"' do
-    click_link "remove"
+  step 'I click link "Delete"' do
+    click_link "Delete"
   end
 
   step 'I submit new snippet "Snippet three"' do
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index cb100ca0f549d7dbc878f52a581a6b1812235da8..84725b9b585444759177ae7383729c808f5f9c23 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -78,6 +78,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
     fill_in :file_name, with: 'Spaces Not Allowed'
   end
 
+  step 'I fill the new file name with a new directory' do
+    fill_in :file_name, with: new_file_name_with_directory
+  end
+
   step 'I fill the commit message' do
     fill_in :commit_message, with: 'Not yet a commit message.', visible: true
   end
@@ -238,6 +242,11 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
       @project.namespace, @project, 'master/' + new_file_name))
   end
 
+  step 'I am redirected to the new file with directory' do
+    expect(current_path).to eq(namespace_project_blob_path(
+      @project.namespace, @project, 'master/' + new_file_name_with_directory))
+  end
+
   step 'I am redirected to the new file on new branch' do
     expect(current_path).to eq(namespace_project_blob_path(
       @project.namespace, @project, 'new_branch_name/' + new_file_name))
@@ -286,6 +295,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
     select "'test'", from: 'ref'
   end
 
+  step "I switch ref to fix" do
+    select "fix", from: 'ref'
+  end
+
   step "I see the ref 'test' has been selected" do
     expect(page).to have_selector '.select2-chosen', text: "'test'"
   end
@@ -294,11 +307,20 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
     visit namespace_project_tree_path(@project.namespace, @project, "'test'")
   end
 
+  step "I visit the fix tree" do
+    visit namespace_project_tree_path(@project.namespace, @project, "fix/.testdir")
+  end
+
   step 'I see the commit data' do
     expect(page).to have_css('.tree-commit-link', visible: true)
     expect(page).not_to have_content('Loading commit data...')
   end
 
+  step 'I see the commit data for a directory with a leading dot' do
+    expect(page).to have_css('.tree-commit-link', visible: true)
+    expect(page).not_to have_content('Loading commit data...')
+  end
+
   private
 
   def set_new_content
@@ -322,6 +344,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
     'not_a_file.md'
   end
 
+  # Constant value that is a valid filename with directory and
+  # not a filename present at root of the seed repository.
+  def new_file_name_with_directory
+    'foo/bar/baz.txt'
+  end
+
   # Constant value that is a valid directory and
   # not a directory present at root of the seed repository.
   def new_dir_name
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index 5744e455ebd2f27382e583cb4306bde3ae86f47d..7021fac5fe4b4c5ab0b0548aa4cf16f921bb75e4 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -206,4 +206,11 @@ module SharedProject
     project = Project.find_by(name: "Shop")
     create :ci_commit, gl_project: project, sha: project.commit.sha
   end
+
+  step 'I should see last commit with CI status' do
+    page.within ".project-last-commit" do
+      expect(page).to have_content(project.commit.sha[0..6])
+      expect(page).to have_content("skipped")
+    end
+  end
 end
diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb
index c67e5e4a06a890d5acbac404b4e0db3db09e7dd1..33ff7084e30df9f45eb6dc46846f0d940918a4f9 100644
--- a/features/steps/shared/project_tab.rb
+++ b/features/steps/shared/project_tab.rb
@@ -46,7 +46,7 @@ module SharedProjectTab
 
   step 'the active main tab should be Settings' do
     page.within '.nav-sidebar' do
-      expect(page).to have_content('Back to project')
+      expect(page).to have_content('Go to project')
     end
   end
 
diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb
index 6ff48e0c6b8eed0405532831d94699c5197af1f3..80d1ddeef055177d7df0d3ef3e2e46843e2a3609 100644
--- a/features/steps/snippets/snippets.rb
+++ b/features/steps/snippets/snippets.rb
@@ -13,13 +13,13 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
   end
 
   step 'I click link "Edit"' do
-    page.within ".file-title" do
+    page.within ".page-title" do
       click_link "Edit"
     end
   end
 
-  step 'I click link "Destroy"' do
-    click_link "remove"
+  step 'I click link "Delete"' do
+    click_link "Delete"
   end
 
   step 'I submit new snippet "Personal snippet three"' do
diff --git a/features/steps/snippets/user.rb b/features/steps/snippets/user.rb
index dea3256229fca2f612dd069beb4ca6110bfafa22..997c605bce2e595787fd5d8c6ba32273d105592a 100644
--- a/features/steps/snippets/user.rb
+++ b/features/steps/snippets/user.rb
@@ -32,19 +32,19 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps
   end
 
   step 'I click "Internal" filter' do
-    page.within('.nav-tabs') do
+    page.within('.snippet-scope-menu') do
       click_link "Internal"
     end
   end
 
   step 'I click "Private" filter' do
-    page.within('.nav-tabs') do
+    page.within('.snippet-scope-menu') do
       click_link "Private"
     end
   end
 
   step 'I click "Public" filter' do
-    page.within('.nav-tabs') do
+    page.within('.snippet-scope-menu') do
       click_link "Public"
     end
   end
diff --git a/lib/api/api.rb b/lib/api/api.rb
index afc0402f9e1d3462ba0b800f1171aed3b087dfce..40671e2517c16c97fa627c437eb72dd12dbe4d7e 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -25,7 +25,7 @@ module API
     format :json
     content_type :txt, "text/plain"
 
-    helpers APIHelpers
+    helpers Helpers
 
     mount Groups
     mount GroupMembers
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 883a5e14b17cea75fc45ed50d82b9fadf7533c19..20cadae2291f7ebb52fbc6818fd6de7ba5999290 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -231,7 +231,7 @@ module API
 
     class CommitStatus < Grape::Entity
       expose :id, :sha, :ref, :status, :name, :target_url, :description,
-             :created_at, :started_at, :finished_at
+             :created_at, :started_at, :finished_at, :allow_failure
       expose :author, using: Entities::UserBasic
     end
 
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 308c84dd13586c6d5d09d504fa4d3a14a452e7aa..a7a768f8895ded69e5bfeba3c9e38819af79e6d6 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -43,7 +43,8 @@ module API
       #   "content": "IyA9PSBTY2hlbWEgSW5mb3...",
       #   "ref": "master",
       #   "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
-      #   "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50"
+      #   "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50",
+      #   "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d",
       # }
       #
       get ":id/repository/files" do
@@ -71,6 +72,7 @@ module API
             ref: ref,
             blob_id: blob.id,
             commit_id: commit.id,
+            last_commit_id: user_project.repository.last_commit_for_path(commit.sha, file_path).id
           }
         else
           not_found! 'File'
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 549b1f9e9a7090d8ca205e54afda4c90834e0886..652bdf9b2781b91d6fed42db5e4c716a67014b4b 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -1,5 +1,5 @@
 module API
-  module APIHelpers
+  module Helpers
     PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
     PRIVATE_TOKEN_PARAM = :private_token
     SUDO_HEADER ="HTTP_SUDO"
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index f3a59fadf24c12b8581aafa6d3af827c49993317..6eb84baf9cb0aab677a820a2f4b05ad861e832bd 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -249,8 +249,16 @@ module API
         required_attributes! [:note]
 
         merge_request = user_project.merge_requests.find(params[:merge_request_id])
-        note = merge_request.notes.new(note: params[:note], project_id: user_project.id)
-        note.author = current_user
+
+        authorize! :create_note, merge_request
+
+        opts = {
+          note: params[:note],
+          noteable_type: 'MergeRequest',
+          noteable_id: merge_request.id
+        }
+
+        note = ::Notes::CreateService.new(user_project, current_user, opts).execute
 
         if note.save
           present note, with: Entities::MRNote
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index c2fb36b41433eeffe4d3fa568f9777d7e137bcaf..67ee66a2058a2be885df8cfe1d820a922d9060b5 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -246,8 +246,8 @@ module API
       # Example Request:
       #  DELETE /projects/:id/fork
       delete ":id/fork" do
-        authenticated_as_admin!
-        unless user_project.forked_project_link.nil?
+        authorize! :remove_fork_project, user_project
+        if user_project.forked?
           user_project.forked_project_link.destroy
         end
       end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 2d96c9666d2a907119fecdfcf8948a19e16a0478..20d568cf4626ed9b7b292a11c066c30b0daccc6e 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -133,7 +133,7 @@ module API
         authorize! :download_code, user_project
 
         begin
-          file_path = ArchiveRepositoryService.new(
+          ArchiveRepositoryService.new(
             user_project,
             params[:sha],
             params[:format]
@@ -141,17 +141,6 @@ module API
         rescue
           not_found!('File')
         end
-
-        if file_path && File.exists?(file_path)
-          data = File.open(file_path, 'rb').read
-          basename = File.basename(file_path)
-          header['Content-Disposition'] = "attachment; filename=\"#{basename}\""
-          content_type MIME::Types.type_for(file_path).first.content_type
-          env['api.format'] = :binary
-          present data
-        else
-          redirect request.fullpath
-        end
       end
 
       # Compare two branches, tags or commits
diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb
index 6f56f680bb9bcba8cde7104146743184936c7648..635967f4bd465949847382d6e0b030b07c9144e5 100644
--- a/lib/backup/builds.rb
+++ b/lib/backup/builds.rb
@@ -1,34 +1,13 @@
-module Backup
-  class Builds
-    attr_reader :app_builds_dir, :backup_builds_dir, :backup_dir
+require 'backup/files'
 
+module Backup
+  class Builds < Files
     def initialize
-      @app_builds_dir = Settings.gitlab_ci.builds_path
-      @backup_dir = Gitlab.config.backup.path
-      @backup_builds_dir = File.join(Gitlab.config.backup.path, 'builds')
-    end
-
-    # Copy builds from builds directory to backup/builds
-    def dump
-      FileUtils.rm_rf(backup_builds_dir)
-      # Ensure the parent dir of backup_builds_dir exists
-      FileUtils.mkdir_p(Gitlab.config.backup.path)
-      # Fail if somebody raced to create backup_builds_dir before us
-      FileUtils.mkdir(backup_builds_dir, mode: 0700)
-      FileUtils.cp_r(app_builds_dir, backup_dir)
-    end
-
-    def restore
-      backup_existing_builds_dir
-
-      FileUtils.cp_r(backup_builds_dir, app_builds_dir)
+      super('builds', Settings.gitlab_ci.builds_path)
     end
 
-    def backup_existing_builds_dir
-      timestamped_builds_path = File.join(app_builds_dir, '..', "builds.#{Time.now.to_i}")
-      if File.exists?(app_builds_dir)
-        FileUtils.mv(app_builds_dir, File.expand_path(timestamped_builds_path))
-      end
+    def create_files_dir
+      Dir.mkdir(app_files_dir, 0700)
     end
   end
 end
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index 959ac4b7868d1815b6ccece264011de2b30625fe..67b2a64bd103d9a602a8f54f60b3564d9aed284f 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -2,26 +2,26 @@ require 'yaml'
 
 module Backup
   class Database
-    attr_reader :config, :db_dir
+    attr_reader :config, :db_file_name
 
     def initialize
       @config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env]
-      @db_dir = File.join(Gitlab.config.backup.path, 'db')
+      @db_file_name = File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz')
     end
 
     def dump
-      FileUtils.rm_rf(@db_dir)
-      # Ensure the parent dir of @db_dir exists
-      FileUtils.mkdir_p(Gitlab.config.backup.path)
-      # Fail if somebody raced to create @db_dir before us
-      FileUtils.mkdir(@db_dir, mode: 0700)
+      FileUtils.mkdir_p(File.dirname(db_file_name))
+      FileUtils.rm_f(db_file_name)
+      compress_rd, compress_wr = IO.pipe
+      compress_pid = spawn(*%W(gzip -1 -c), in: compress_rd, out: [db_file_name, 'w', 0600])
+      compress_rd.close
 
-      success = case config["adapter"]
+      dump_pid = case config["adapter"]
       when /^mysql/ then
         $progress.print "Dumping MySQL database #{config['database']} ... "
         # Workaround warnings from MySQL 5.6 about passwords on cmd line
         ENV['MYSQL_PWD'] = config["password"].to_s if config["password"]
-        system('mysqldump', *mysql_args, config['database'], out: db_file_name)
+        spawn('mysqldump', *mysql_args, config['database'], out: compress_wr)
       when "postgresql" then
         $progress.print "Dumping PostgreSQL database #{config['database']} ... "
         pg_env
@@ -30,48 +30,42 @@ module Backup
           pgsql_args << "-n"
           pgsql_args << Gitlab.config.backup.pg_schema
         end
-        system('pg_dump', *pgsql_args, config['database'], out: db_file_name)
+        spawn('pg_dump', *pgsql_args, config['database'], out: compress_wr)
       end
-      report_success(success)
-      abort 'Backup failed' unless success
+      compress_wr.close
+
+      success = [compress_pid, dump_pid].all? { |pid| Process.waitpid(pid); $?.success? }
 
-      $progress.print 'Compressing database ... '
-      success = system('gzip', db_file_name)
       report_success(success)
-      abort 'Backup failed: compress error' unless success
+      abort 'Backup failed' unless success
     end
 
     def restore
-      $progress.print 'Decompressing database ... '
-      success = system('gzip', '-d', db_file_name_gz)
-      report_success(success)
-      abort 'Restore failed: decompress error' unless success
+      decompress_rd, decompress_wr = IO.pipe
+      decompress_pid = spawn(*%W(gzip -cd), out: decompress_wr, in: db_file_name)
+      decompress_wr.close
 
-      success = case config["adapter"]
+      restore_pid = case config["adapter"]
       when /^mysql/ then
         $progress.print "Restoring MySQL database #{config['database']} ... "
         # Workaround warnings from MySQL 5.6 about passwords on cmd line
         ENV['MYSQL_PWD'] = config["password"].to_s if config["password"]
-        system('mysql', *mysql_args, config['database'], in: db_file_name)
+        spawn('mysql', *mysql_args, config['database'], in: decompress_rd)
       when "postgresql" then
         $progress.print "Restoring PostgreSQL database #{config['database']} ... "
         pg_env
-        system('psql', config['database'], '-f', db_file_name)
+        spawn('psql', config['database'], in: decompress_rd)
       end
+      decompress_rd.close
+
+      success = [decompress_pid, restore_pid].all? { |pid| Process.waitpid(pid); $?.success? }
+
       report_success(success)
       abort 'Restore failed' unless success
     end
 
     protected
 
-    def db_file_name
-      File.join(db_dir, 'database.sql')
-    end
-
-    def db_file_name_gz
-      File.join(db_dir, 'database.sql.gz')
-    end
-
     def mysql_args
       args = {
         'host'      => '--host',
diff --git a/lib/backup/files.rb b/lib/backup/files.rb
new file mode 100644
index 0000000000000000000000000000000000000000..654b4d1c8962dc35300e7590c90e3cdf88dc9baa
--- /dev/null
+++ b/lib/backup/files.rb
@@ -0,0 +1,40 @@
+require 'open3'
+
+module Backup
+  class Files
+    attr_reader :name, :app_files_dir, :backup_tarball, :files_parent_dir
+
+    def initialize(name, app_files_dir)
+      @name = name
+      @app_files_dir = File.realpath(app_files_dir)
+      @files_parent_dir = File.realpath(File.join(@app_files_dir, '..'))
+      @backup_tarball = File.join(Gitlab.config.backup.path, name + '.tar.gz')
+    end
+
+    # Copy files from public/files to backup/files
+    def dump
+      FileUtils.mkdir_p(Gitlab.config.backup.path)
+      FileUtils.rm_f(backup_tarball)
+      run_pipeline!([%W(tar -C #{app_files_dir} -cf - .), %W(gzip -c -1)], out: [backup_tarball, 'w', 0600])
+    end
+
+    def restore
+      backup_existing_files_dir
+      create_files_dir
+
+      run_pipeline!([%W(gzip -cd), %W(tar -C #{app_files_dir} -xf -)], in: backup_tarball)
+    end
+
+    def backup_existing_files_dir
+      timestamped_files_path = File.join(files_parent_dir, "#{name}.#{Time.now.to_i}")
+      if File.exists?(app_files_dir)
+        FileUtils.mv(app_files_dir, File.expand_path(timestamped_files_path))
+      end
+    end
+
+    def run_pipeline!(cmd_list, options={})
+      status_list = Open3.pipeline(*cmd_list, options)
+      abort 'Backup failed' unless status_list.compact.all?(&:success?)
+    end
+  end
+end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 5c42f25f4a267a3fbd8ad10dd8deb4f8ffe64bba..f011fd03de0b06c001d6b75b85d2ca1ef0bff226 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -150,11 +150,11 @@ module Backup
     private
 
     def backup_contents
-      folders_to_backup + ["backup_information.yml"]
+      folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "backup_information.yml"]
     end
 
     def folders_to_backup
-      folders = %w{repositories db uploads builds}
+      folders = %w{repositories db}
 
       if ENV["SKIP"]
         return folders.reject{ |folder| ENV["SKIP"].include?(folder) }
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index 4d70f7883ddb8bd9698af80677416766013fa355..a82a7e1f7bf70e51f90c112015941b68913587ff 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -35,7 +35,7 @@ module Backup
           if wiki.repository.empty?
             $progress.puts " [SKIPPED]".cyan
           else
-            cmd = %W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all)
+            cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all)
             output, status = Gitlab::Popen.popen(cmd)
             if status.zero?
               $progress.puts " [DONE]".green
@@ -67,7 +67,7 @@ module Backup
           FileUtils.mkdir_p(path_to_repo(project))
           cmd = %W(tar -xf #{path_to_bundle(project)} -C #{path_to_repo(project)})
         else
-          cmd = %W(git init --bare #{path_to_repo(project)})
+          cmd = %W(#{Gitlab.config.git.bin_path} init --bare #{path_to_repo(project)})
         end
 
         if system(*cmd, silent)
@@ -87,7 +87,7 @@ module Backup
           # that was initialized with ProjectWiki.new() and then
           # try to restore with 'git clone --bare'.
           FileUtils.rm_rf(path_to_repo(wiki))
-          cmd = %W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)})
+          cmd = %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)})
 
           if system(*cmd, silent)
             $progress.puts " [DONE]".green
diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb
index 1f9626644e616c6a7d3d70a53d727c02e20459a0..9261f77f3c9d45ee137dd450ba5a6c401267c592 100644
--- a/lib/backup/uploads.rb
+++ b/lib/backup/uploads.rb
@@ -1,34 +1,14 @@
+require 'backup/files'
+
 module Backup
-  class Uploads
-    attr_reader :app_uploads_dir, :backup_uploads_dir, :backup_dir
+  class Uploads < Files
 
     def initialize
-      @app_uploads_dir = File.realpath(Rails.root.join('public', 'uploads'))
-      @backup_dir = Gitlab.config.backup.path
-      @backup_uploads_dir = File.join(Gitlab.config.backup.path, 'uploads')
-    end
-
-    # Copy uploads from public/uploads to backup/uploads
-    def dump
-      FileUtils.rm_rf(backup_uploads_dir)
-      # Ensure the parent dir of backup_uploads_dir exists
-      FileUtils.mkdir_p(Gitlab.config.backup.path)
-      # Fail if somebody raced to create backup_uploads_dir before us
-      FileUtils.mkdir(backup_uploads_dir, mode: 0700)
-      FileUtils.cp_r(app_uploads_dir, backup_dir)
-    end
-
-    def restore
-      backup_existing_uploads_dir
-
-      FileUtils.cp_r(backup_uploads_dir, app_uploads_dir)
+      super('uploads', Rails.root.join('public/uploads'))
     end
 
-    def backup_existing_uploads_dir
-      timestamped_uploads_path = File.join(app_uploads_dir, '..', "uploads.#{Time.now.to_i}")
-      if File.exists?(app_uploads_dir)
-        FileUtils.mv(app_uploads_dir, File.expand_path(timestamped_uploads_path))
-      end
+    def create_files_dir
+      Dir.mkdir(app_files_dir)
     end
   end
 end
diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb
index 218d8c3adccbfb5b32f2610cb6c6a1441e93e558..0a4cbf69b63780cea701c59f2cf0b3db4e1f6de2 100644
--- a/lib/ci/api/api.rb
+++ b/lib/ci/api/api.rb
@@ -26,7 +26,7 @@ module Ci
       format :json
 
       helpers Helpers
-      helpers ::API::APIHelpers
+      helpers ::API::Helpers
 
       mount Builds
       mount Commits
diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb
index e602cda81d610402356e8e0654752c8ddecb1fdf..7e4986b6af35aaaab4078ceda5f55c9932d8a5b3 100644
--- a/lib/ci/api/helpers.rb
+++ b/lib/ci/api/helpers.rb
@@ -16,7 +16,9 @@ module Ci
       end
 
       def update_runner_last_contact
-        if current_runner.contacted_at.nil? || Time.now - current_runner.contacted_at >= UPDATE_RUNNER_EVERY
+        # Use a random threshold to prevent beating DB updates
+        contacted_at_max_age = UPDATE_RUNNER_EVERY + Random.rand(UPDATE_RUNNER_EVERY)
+        if current_runner.contacted_at.nil? || Time.now - current_runner.contacted_at >= contacted_at_max_age
           current_runner.update_attributes(contacted_at: Time.now)
         end
       end
diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb
index 915a4f526a6f4a334ec107a89a188ca5fd7383fc..5ff7407c6fe7775244b9bf85d8c0e59c47e234c4 100644
--- a/lib/ci/charts.rb
+++ b/lib/ci/charts.rb
@@ -60,7 +60,8 @@ module Ci
 
     class BuildTime < Chart
       def collect
-        commits = project.commits.joins(:builds).where("#{Ci::Build.table_name}.finished_at is NOT NULL AND #{Ci::Build.table_name}.started_at is NOT NULL").last(30)
+        commits = project.commits.last(30)
+
         commits.each do |commit|
           @labels << commit.short_sha
           @build_times << (commit.duration / 60)
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index c47951bc5d190e108e89bfc8b95e4afb851cbf00..0f57a4f53ab23170d1b33176ed744c883016e6a3 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -5,12 +5,13 @@ module Ci
     DEFAULT_STAGES = %w(build test deploy)
     DEFAULT_STAGE = 'test'
     ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables]
-    ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage]
+    ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when]
 
-    attr_reader :before_script, :image, :services, :variables
+    attr_reader :before_script, :image, :services, :variables, :path
 
-    def initialize(config)
+    def initialize(config, path = nil)
       @config = YAML.load(config)
+      @path = path
 
       unless @config.is_a? Hash
         raise ValidationError, "YAML should be a hash"
@@ -63,26 +64,6 @@ module Ci
       end
     end
 
-    def process?(only_params, except_params, ref, tag)
-      return true if only_params.nil? && except_params.nil?
-
-      if only_params
-        return true if tag && only_params.include?("tags")
-        return true if !tag && only_params.include?("branches")
-        
-        only_params.find do |pattern|
-          match_ref?(pattern, ref)
-        end
-      else
-        return false if tag && except_params.include?("tags")
-        return false if !tag && except_params.include?("branches")
-
-        except_params.each do |pattern|
-          return false if match_ref?(pattern, ref)
-        end
-      end
-    end
-
     def build_job(name, job)
       {
         stage_idx: stages.index(job[:stage]),
@@ -93,6 +74,7 @@ module Ci
         only: job[:only],
         except: job[:except],
         allow_failure: job[:allow_failure] || false,
+        when: job[:when] || 'on_success',
         options: {
           image: job[:image] || @image,
           services: job[:services] || @services
@@ -100,14 +82,6 @@ module Ci
       }
     end
 
-    def match_ref?(pattern, ref)
-      if pattern.first == "/" && pattern.last == "/"
-        Regexp.new(pattern[1...-1]) =~ ref
-      else
-        pattern == ref
-      end
-    end
-
     def normalize_script(script)
       if script.is_a? Array
         script.join("\n")
@@ -138,62 +112,105 @@ module Ci
       end
 
       @jobs.each do |name, job|
-        validate_job!("#{name} job", job)
+        validate_job!(name, job)
       end
 
       true
     end
 
     def validate_job!(name, job)
+      if name.blank? || !validate_string(name)
+        raise ValidationError, "job name should be non-empty string"
+      end
+
       job.keys.each do |key|
         unless ALLOWED_JOB_KEYS.include? key
-          raise ValidationError, "#{name}: unknown parameter #{key}"
+          raise ValidationError, "#{name} job: unknown parameter #{key}"
         end
       end
 
-      if !job[:script].is_a?(String) && !validate_array_of_strings(job[:script])
-        raise ValidationError, "#{name}: script should be a string or an array of a strings"
+      if !validate_string(job[:script]) && !validate_array_of_strings(job[:script])
+        raise ValidationError, "#{name} job: script should be a string or an array of a strings"
       end
 
       if job[:stage]
         unless job[:stage].is_a?(String) && job[:stage].in?(stages)
-          raise ValidationError, "#{name}: stage parameter should be #{stages.join(", ")}"
+          raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}"
         end
       end
 
-      if job[:image] && !job[:image].is_a?(String)
-        raise ValidationError, "#{name}: image should be a string"
+      if job[:image] && !validate_string(job[:image])
+        raise ValidationError, "#{name} job: image should be a string"
       end
 
       if job[:services] && !validate_array_of_strings(job[:services])
-        raise ValidationError, "#{name}: services should be an array of strings"
+        raise ValidationError, "#{name} job: services should be an array of strings"
       end
 
       if job[:tags] && !validate_array_of_strings(job[:tags])
-        raise ValidationError, "#{name}: tags parameter should be an array of strings"
+        raise ValidationError, "#{name} job: tags parameter should be an array of strings"
       end
 
       if job[:only] && !validate_array_of_strings(job[:only])
-        raise ValidationError, "#{name}: only parameter should be an array of strings"
+        raise ValidationError, "#{name} job: only parameter should be an array of strings"
       end
 
       if job[:except] && !validate_array_of_strings(job[:except])
-        raise ValidationError, "#{name}: except parameter should be an array of strings"
+        raise ValidationError, "#{name} job: except parameter should be an array of strings"
       end
 
       if job[:allow_failure] && !job[:allow_failure].in?([true, false])
-        raise ValidationError, "#{name}: allow_failure parameter should be an boolean"
+        raise ValidationError, "#{name} job: allow_failure parameter should be an boolean"
+      end
+
+      if job[:when] && !job[:when].in?(%w(on_success on_failure always))
+        raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always"
       end
     end
 
     private
 
     def validate_array_of_strings(values)
-      values.is_a?(Array) && values.all? {|tag| tag.is_a?(String)}
+      values.is_a?(Array) && values.all? { |value| validate_string(value) }
     end
 
     def validate_variables(variables)
-      variables.is_a?(Hash) && variables.all? {|key, value| key.is_a?(Symbol) && value.is_a?(String)}
+      variables.is_a?(Hash) && variables.all? { |key, value| validate_string(key) && validate_string(value) }
+    end
+
+    def validate_string(value)
+      value.is_a?(String) || value.is_a?(Symbol)
+    end
+
+    def process?(only_params, except_params, ref, tag)
+      if only_params.present?
+        return false unless matching?(only_params, ref, tag)
+      end
+
+      if except_params.present?
+        return false if matching?(except_params, ref, tag)
+      end
+
+      true
+    end
+
+    def matching?(patterns, ref, tag)
+      patterns.any? do |pattern|
+        match_ref?(pattern, ref, tag)
+      end
+    end
+
+    def match_ref?(pattern, ref, tag)
+      pattern, path = pattern.split('@', 2)
+      return false if path && path != self.path
+      return true if tag && pattern == 'tags'
+      return true if !tag && pattern == 'branches'
+
+      if pattern.first == "/" && pattern.last == "/"
+        Regexp.new(pattern[1...-1]) =~ ref
+      else
+        pattern == ref
+      end
     end
   end
 end
diff --git a/lib/ci/migrate/builds.rb b/lib/ci/migrate/builds.rb
deleted file mode 100644
index c4f62e552959481d8fd9dce257de43dc73523bd3..0000000000000000000000000000000000000000
--- a/lib/ci/migrate/builds.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-module Ci
-  module Migrate
-    class Builds
-      attr_reader :app_builds_dir, :backup_builds_tarball, :backup_dir
-
-      def initialize
-        @app_builds_dir = Settings.gitlab_ci.builds_path
-        @backup_dir = Gitlab.config.backup.path
-        @backup_builds_tarball = File.join(backup_dir, 'builds/builds.tar.gz')
-      end
-
-      def restore
-        backup_existing_builds_dir
-
-        FileUtils.mkdir_p(app_builds_dir, mode: 0700)
-        unless system('tar', '-C', app_builds_dir, '-zxf', backup_builds_tarball)
-          abort 'Restore failed'.red
-        end
-      end
-
-      def backup_existing_builds_dir
-        timestamped_builds_path = File.join(app_builds_dir, '..', "builds.#{Time.now.to_i}")
-        if File.exists?(app_builds_dir)
-          FileUtils.mv(app_builds_dir, File.expand_path(timestamped_builds_path))
-        end
-      end
-    end
-  end
-end
diff --git a/lib/ci/migrate/database.rb b/lib/ci/migrate/database.rb
deleted file mode 100644
index bf9b80f1f624f97e0a61183b204ae0059b0184b6..0000000000000000000000000000000000000000
--- a/lib/ci/migrate/database.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-require 'yaml'
-
-module Ci
-  module Migrate
-    class Database
-      attr_reader :config
-
-      def initialize
-        @config = YAML.load_file(File.join(Rails.root, 'config', 'database.yml'))[Rails.env]
-      end
-
-      def restore
-        decompress_rd, decompress_wr = IO.pipe
-        decompress_pid = spawn(*%W(gzip -cd), out: decompress_wr, in: db_file_name)
-        decompress_wr.close
-
-        restore_pid = case config["adapter"]
-                        when /^mysql/ then
-                          $progress.print "Restoring MySQL database #{config['database']} ... "
-                          # Workaround warnings from MySQL 5.6 about passwords on cmd line
-                          ENV['MYSQL_PWD'] = config["password"].to_s if config["password"]
-                          spawn('mysql', *mysql_args, config['database'], in: decompress_rd)
-                        when "postgresql" then
-                          $progress.print "Restoring PostgreSQL database #{config['database']} ... "
-                          pg_env
-                          spawn('psql', config['database'], in: decompress_rd)
-                      end
-        decompress_rd.close
-
-        success = [decompress_pid, restore_pid].all? { |pid| Process.waitpid(pid); $?.success? }
-        abort 'Restore failed' unless success
-      end
-
-      protected
-
-      def db_file_name
-        File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz')
-      end
-
-      def mysql_args
-        args = {
-          'host' => '--host',
-          'port' => '--port',
-          'socket' => '--socket',
-          'username' => '--user',
-          'encoding' => '--default-character-set'
-        }
-        args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
-      end
-
-      def pg_env
-        ENV['PGUSER'] = config["username"] if config["username"]
-        ENV['PGHOST'] = config["host"] if config["host"]
-        ENV['PGPORT'] = config["port"].to_s if config["port"]
-        ENV['PGPASSWORD'] = config["password"].to_s if config["password"]
-      end
-
-      def report_success(success)
-        if success
-          puts '[DONE]'.green
-        else
-          puts '[FAILED]'.red
-        end
-      end
-    end
-  end
-end
diff --git a/lib/ci/migrate/manager.rb b/lib/ci/migrate/manager.rb
deleted file mode 100644
index e5e4fb784eb9c92dd5f41c1c6f84d5a3ebc4ef01..0000000000000000000000000000000000000000
--- a/lib/ci/migrate/manager.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-module Ci
-  module Migrate
-    class Manager
-      CI_IMPORT_PREFIX = '8.0' # Only allow imports from CI 8.0.x
-
-      def cleanup
-        $progress.print "Deleting tmp directories ... "
-
-        backup_contents.each do |dir|
-          next unless File.exist?(File.join(Gitlab.config.backup.path, dir))
-
-          if FileUtils.rm_rf(File.join(Gitlab.config.backup.path, dir))
-            $progress.puts "done".green
-          else
-            puts "deleting tmp directory '#{dir}' failed".red
-            abort 'Backup failed'
-          end
-        end
-      end
-
-      def unpack
-        Dir.chdir(Gitlab.config.backup.path)
-
-        # check for existing backups in the backup dir
-        file_list = Dir.glob("*_gitlab_ci_backup.tar").each.map { |f| f.split(/_/).first.to_i }
-        puts "no backups found" if file_list.count == 0
-
-        if file_list.count > 1 && ENV["BACKUP"].nil?
-          puts "Found more than one backup, please specify which one you want to restore:"
-          puts "rake gitlab:backup:restore BACKUP=timestamp_of_backup"
-          exit 1
-        end
-
-        tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_ci_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_ci_backup.tar")
-
-        unless File.exists?(tar_file)
-          puts "The specified CI backup doesn't exist!"
-          exit 1
-        end
-
-        $progress.print "Unpacking backup ... "
-
-        unless Kernel.system(*%W(tar -xf #{tar_file}))
-          puts "unpacking backup failed".red
-          exit 1
-        else
-          $progress.puts "done".green
-        end
-
-        ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0
-
-        # restoring mismatching backups can lead to unexpected problems
-        if !settings[:gitlab_version].start_with?(CI_IMPORT_PREFIX)
-          puts "GitLab CI version mismatch:".red
-          puts "  Your current GitLab CI version (#{GitlabCi::VERSION}) differs from the GitLab CI (#{settings[:gitlab_version]}) version in the backup!".red
-          exit 1
-        end
-      end
-
-      private
-
-      def backup_contents
-        ["db", "builds", "backup_information.yml"]
-      end
-
-      def settings
-        @settings ||= YAML.load_file("backup_information.yml")
-      end
-    end
-  end
-end
-
diff --git a/lib/ci/migrate/tags.rb b/lib/ci/migrate/tags.rb
deleted file mode 100644
index 97e043ece27f47b7ebf92c72db7399a58f8143ee..0000000000000000000000000000000000000000
--- a/lib/ci/migrate/tags.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'yaml'
-
-module Ci
-  module Migrate
-    class Tags
-      def restore
-        puts 'Inserting tags...'
-        connection.select_all('SELECT ci_tags.name FROM ci_tags').each do |tag|
-          begin
-            connection.execute("INSERT INTO tags (name) VALUES(#{ActiveRecord::Base::sanitize(tag['name'])})")
-          rescue ActiveRecord::RecordNotUnique
-          end
-        end
-
-        ActiveRecord::Base.transaction do
-          puts 'Deleting old taggings...'
-          connection.execute "DELETE FROM taggings WHERE context = 'tags' AND taggable_type LIKE 'Ci::%'"
-
-          puts 'Inserting taggings...'
-          connection.execute(
-            'INSERT INTO taggings (taggable_type, taggable_id, tag_id, context) ' +
-              "SELECT CONCAT('Ci::', ci_taggings.taggable_type), ci_taggings.taggable_id, tags.id, 'tags' FROM ci_taggings " +
-              'JOIN ci_tags ON ci_tags.id = ci_taggings.tag_id ' +
-              'JOIN tags ON tags.name = ci_tags.name '
-          )
-
-          puts 'Resetting counters... '
-          connection.execute(
-            'UPDATE tags SET ' +
-              'taggings_count = (SELECT COUNT(*) FROM taggings WHERE tags.id = taggings.tag_id)'
-          )
-        end
-      end
-
-      protected
-
-      def connection
-        ActiveRecord::Base.connection
-      end
-    end
-  end
-end
diff --git a/lib/ci/status.rb b/lib/ci/status.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c02b3b8f3e4c6666051edbacae4e0a7620de187e
--- /dev/null
+++ b/lib/ci/status.rb
@@ -0,0 +1,21 @@
+module Ci
+  class Status
+    def self.get_status(statuses)
+      statuses.reject! { |status| status.try(&:allow_failure?) }
+
+      if statuses.none?
+        'skipped'
+      elsif statuses.all?(&:success?)
+        'success'
+      elsif statuses.all?(&:pending?)
+        'pending'
+      elsif statuses.any?(&:running?) || statuses.any?(&:pending?)
+        'running'
+      elsif statuses.all?(&:canceled?)
+        'canceled'
+      else
+        'failed'
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 0353b3b7ed3dc627661ba1b3d0bc1e5a865d1655..440ef5a3cb35d795a56519b7b37b1f7c98cbe5e7 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -34,7 +34,7 @@ module Grack
       auth!
 
       if project && authorized_request?
-        # Tell gitlab-git-http-server the request is OK, and what the GL_ID is
+        # Tell gitlab-workhorse the request is OK, and what the GL_ID is
         render_grack_auth_ok
       elsif @user.nil? && !@ci
         unauthorized
@@ -193,7 +193,21 @@ module Grack
     end
 
     def render_grack_auth_ok
-      [200, { "Content-Type" => "application/json" }, [JSON.dump({ 'GL_ID' => Gitlab::ShellEnv.gl_id(@user) })]]
+      repo_path =
+        if @request.path_info =~ /^([\w\.\/-]+)\.wiki\.git/
+          ProjectWiki.new(project).repository.path_to_repo
+        else
+          project.repository.path_to_repo
+        end
+
+      [
+        200,
+        { "Content-Type" => "application/json" },
+        [JSON.dump({
+          'GL_ID' => Gitlab::ShellEnv.gl_id(@user),
+          'RepoPath' => repo_path,
+        })]
+      ]
     end
 
     def render_not_found
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 0ea1b6a2f6f4f86923dcd324077ce624e6886dae..cd84afa31d56ad9ec625e69400a53bff4fc27d39 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -23,7 +23,8 @@ module Gitlab
         restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
         max_attachment_size: Settings.gitlab['max_attachment_size'],
         session_expire_delay: Settings.gitlab['session_expire_delay'],
-        import_sources: Settings.gitlab['import_sources']
+        import_sources: Settings.gitlab['import_sources'],
+        shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
       )
     end
 
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 741a52714ac98757dfda549a22577fa6e8d6a9b2..71f37f1fef8b27d5092792d7bd9b77b65541c9be 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -1,7 +1,7 @@
 module Gitlab
   module Database
     def self.mysql?
-      ActiveRecord::Base.connection.adapter_name.downcase == 'mysql'
+      ActiveRecord::Base.connection.adapter_name.downcase == 'mysql2'
     end
 
     def self.postgresql?
diff --git a/lib/gitlab/force_push_check.rb b/lib/gitlab/force_push_check.rb
index fdb6a35c78d02fef0e24c4dc888a90b304249ff3..93c6a5bb7f58ea0c5a56d3f6da1d57cd7c70d25a 100644
--- a/lib/gitlab/force_push_check.rb
+++ b/lib/gitlab/force_push_check.rb
@@ -7,7 +7,7 @@ module Gitlab
       if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
         false
       else
-        missed_refs, _ = Gitlab::Popen.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev}))
+        missed_refs, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev}))
         missed_refs.split("\n").size > 0
       end
     end
diff --git a/lib/gitlab/git_ref_validator.rb b/lib/gitlab/git_ref_validator.rb
index 39d17def93073286b1f37b899dd7c9bba0e4d76e..4d83d8e72a8b3674c3fdb30e689a3b05f5c5b277 100644
--- a/lib/gitlab/git_ref_validator.rb
+++ b/lib/gitlab/git_ref_validator.rb
@@ -6,7 +6,7 @@ module Gitlab
     # Returns true for a valid reference name, false otherwise
     def validate(ref_name)
       Gitlab::Utils.system_silent(
-        %W(git check-ref-format refs/#{ref_name}))
+        %W(#{Gitlab.config.git.bin_path} check-ref-format refs/#{ref_name}))
     end
   end
 end
diff --git a/lib/gitlab/incoming_email.rb b/lib/gitlab/incoming_email.rb
index 856ccc710848110ededb0de9755bbab0d22ff441..9068d79c95e29a0ee1011209ca0434fb3e2516c0 100644
--- a/lib/gitlab/incoming_email.rb
+++ b/lib/gitlab/incoming_email.rb
@@ -24,12 +24,12 @@ module Gitlab
         match[1]
       end
 
-      private
-
       def config
         Gitlab.config.incoming_email
       end
 
+      private
+
       def address_regex
         wildcard_address = config.address
         return nil unless wildcard_address
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index ae5f2544691bd7ba415cf2da80cfc6a623351535..b082bfc434bca43f89185a53e5e72db89015f1a6 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -7,6 +7,14 @@ module Gitlab
   module Markdown
     # Convert a Markdown String into an HTML-safe String of HTML
     #
+    # Note that while the returned HTML will have been sanitized of dangerous
+    # HTML, it may post a risk of information leakage if it's not also passed
+    # through `post_process`.
+    #
+    # Also note that the returned String is always HTML, not XHTML. Views
+    # requiring XHTML, such as Atom feeds, need to call `post_process` on the
+    # result, providing the appropriate `pipeline` option.
+    #
     # markdown - Markdown String
     # context  - Hash of context options passed to our HTML Pipeline
     #
@@ -31,6 +39,33 @@ module Gitlab
       renderer.render(markdown)
     end
 
+    # Perform post-processing on an HTML String
+    #
+    # This method is used to perform state-dependent changes to a String of
+    # HTML, such as removing references that the current user doesn't have
+    # permission to make (`RedactorFilter`).
+    #
+    # html     - String to process
+    # options  - Hash of options to customize output
+    #            :pipeline  - Symbol pipeline type
+    #            :project   - Project
+    #            :user      - User object
+    #
+    # Returns an HTML-safe String
+    def self.post_process(html, options)
+      context = {
+        project:      options[:project],
+        current_user: options[:user]
+      }
+      doc = post_processor.to_document(html, context)
+
+      if options[:pipeline] == :atom
+        doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
+      else
+        doc.to_html
+      end.html_safe
+    end
+
     # 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'
@@ -41,6 +76,7 @@ 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 :RedactorFilter,               'gitlab/markdown/redactor_filter'
     autoload :RelativeLinkFilter,           'gitlab/markdown/relative_link_filter'
     autoload :SanitizationFilter,           'gitlab/markdown/sanitization_filter'
     autoload :SnippetReferenceFilter,       'gitlab/markdown/snippet_reference_filter'
@@ -48,27 +84,22 @@ module Gitlab
     autoload :TableOfContentsFilter,        'gitlab/markdown/table_of_contents_filter'
     autoload :TaskListFilter,               'gitlab/markdown/task_list_filter'
     autoload :UserReferenceFilter,          'gitlab/markdown/user_reference_filter'
+    autoload :UploadLinkFilter,             'gitlab/markdown/upload_link_filter'
 
-    # Public: Parse the provided text with GitLab-Flavored Markdown
+    # Public: Parse the provided HTML with GitLab-Flavored Markdown
+    #
+    # html    - HTML String
+    # options - A Hash of options used to customize output (default: {})
+    #           :no_header_anchors - Disable header anchors in TableOfContentsFilter
+    #           :path              - Current path String
+    #           :pipeline          - Symbol pipeline type
+    #           :project           - Current Project object
+    #           :project_wiki      - Current ProjectWiki object
+    #           :ref               - Current ref String
     #
-    # text         - the source text
-    # options      - A Hash of options used to customize output (default: {}):
-    #                :xhtml               - output XHTML instead of HTML
-    #                :reference_only_path - Use relative path for reference links
-    def self.gfm(text, options = {})
-      return text if text.nil?
-
-      # Duplicate the string so we don't alter the original, then call to_str
-      # to cast it back to a String instead of a SafeBuffer. This is required
-      # for gsub calls to work as we need them to.
-      text = text.dup.to_str
-
-      options.reverse_merge!(
-        xhtml:                false,
-        reference_only_path:  true,
-        project:              options[:project],
-        current_user:         options[:current_user]
-      )
+    # Returns an HTML-safe String
+    def self.gfm(html, options = {})
+      return '' unless html.present?
 
       @pipeline ||= HTML::Pipeline.new(filters)
 
@@ -77,41 +108,36 @@ module Gitlab
         pipeline: options[:pipeline],
 
         # EmojiFilter
-        asset_root: Gitlab.config.gitlab.base_url,
         asset_host: Gitlab::Application.config.asset_host,
-
-        # TableOfContentsFilter
-        no_header_anchors: options[:no_header_anchors],
+        asset_root: Gitlab.config.gitlab.base_url,
 
         # ReferenceFilter
-        current_user:    options[:current_user],
-        only_path:       options[:reference_only_path],
-        project:         options[:project],
+        only_path: only_path_pipeline?(options[:pipeline]),
+        project:   options[:project],
 
         # RelativeLinkFilter
+        project_wiki:   options[:project_wiki],
         ref:            options[:ref],
         requested_path: options[:path],
-        project_wiki:   options[:project_wiki]
-      }
-
-      result = @pipeline.call(text, context)
 
-      save_options = 0
-      if options[:xhtml]
-        save_options |= Nokogiri::XML::Node::SaveOptions::AS_XHTML
-      end
-
-      text = result[:output].to_html(save_with: save_options)
+        # TableOfContentsFilter
+        no_header_anchors: options[:no_header_anchors]
+      }
 
-      text.html_safe
+      @pipeline.to_html(html, context).html_safe
     end
 
     private
 
-    def self.renderer
-      @markdown ||= begin
-        renderer = Redcarpet::Render::HTML.new
-        Redcarpet::Markdown.new(renderer, redcarpet_options)
+    # Check if a pipeline enables the `only_path` context option
+    #
+    # Returns Boolean
+    def self.only_path_pipeline?(pipeline)
+      case pipeline
+      when :atom, :email
+        false
+      else
+        true
       end
     end
 
@@ -129,6 +155,17 @@ module Gitlab
       }.freeze
     end
 
+    def self.renderer
+      @markdown ||= begin
+        renderer = Redcarpet::Render::HTML.new
+        Redcarpet::Markdown.new(renderer, redcarpet_options)
+      end
+    end
+
+    def self.post_processor
+      @post_processor ||= HTML::Pipeline.new([Gitlab::Markdown::RedactorFilter])
+    end
+
     # Filters used in our pipeline
     #
     # SanitizationFilter should come first so that all generated reference HTML
@@ -140,6 +177,7 @@ module Gitlab
         Gitlab::Markdown::SyntaxHighlightFilter,
         Gitlab::Markdown::SanitizationFilter,
 
+        Gitlab::Markdown::UploadLinkFilter,
         Gitlab::Markdown::RelativeLinkFilter,
         Gitlab::Markdown::EmojiFilter,
         Gitlab::Markdown::TableOfContentsFilter,
diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb
index bb496135d923680695d3dc9b37c7a6f070c39311..e070edae0a4c43f7747c4a207955f051ebbe265f 100644
--- a/lib/gitlab/markdown/commit_range_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_range_reference_filter.rb
@@ -26,6 +26,18 @@ module Gitlab
         end
       end
 
+      def self.referenced_by(node)
+        project = Project.find(node.attr("data-project")) rescue nil
+        return unless project
+
+        id = node.attr("data-commit-range")
+        range = CommitRange.new(id, project)
+
+        return unless range.valid_commits?
+
+        { commit_range: range }
+      end
+
       def initialize(*args)
         super
 
@@ -53,13 +65,11 @@ module Gitlab
           range = CommitRange.new(id, project)
 
           if range.valid_commits?
-            push_result(:commit_range, range)
-
             url = url_for_commit_range(project, range)
 
             title = range.reference_title
             klass = reference_class(:commit_range)
-            data  = data_attribute(project.id)
+            data  = data_attribute(project: project.id, commit_range: id)
 
             project_ref += '@' if project_ref
 
diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb
index fcbb2e936a5e3d3b1566f1e896695e60c0fff68c..8cdbeb1f9cf7101b0201a65003c125e9f8e5ffcf 100644
--- a/lib/gitlab/markdown/commit_reference_filter.rb
+++ b/lib/gitlab/markdown/commit_reference_filter.rb
@@ -26,6 +26,18 @@ module Gitlab
         end
       end
 
+      def self.referenced_by(node)
+        project = Project.find(node.attr("data-project")) rescue nil
+        return unless project
+
+        id = node.attr("data-commit")
+        commit = commit_from_ref(project, id)
+
+        return unless commit
+
+        { commit: commit }
+      end
+
       def call
         replace_text_nodes_matching(Commit.reference_pattern) do |content|
           commit_link_filter(content)
@@ -39,17 +51,15 @@ module Gitlab
       # Returns a String with commit references replaced with links. All links
       # have `gfm` and `gfm-commit` class names attached for styling.
       def commit_link_filter(text)
-        self.class.references_in(text) do |match, commit_ref, project_ref|
+        self.class.references_in(text) do |match, id, project_ref|
           project = self.project_from_ref(project_ref)
 
-          if commit = commit_from_ref(project, commit_ref)
-            push_result(:commit, commit)
-
+          if commit = self.class.commit_from_ref(project, id)
             url = url_for_commit(project, commit)
 
             title = escape_once(commit.link_title)
             klass = reference_class(:commit)
-            data  = data_attribute(project.id)
+            data  = data_attribute(project: project.id, commit: id)
 
             project_ref += '@' if project_ref
 
@@ -62,9 +72,9 @@ module Gitlab
         end
       end
 
-      def commit_from_ref(project, commit_ref)
+      def self.commit_from_ref(project, id)
         if project && project.valid_repo?
-          project.commit(commit_ref)
+          project.commit(id)
         end
       end
 
diff --git a/lib/gitlab/markdown/cross_project_reference.rb b/lib/gitlab/markdown/cross_project_reference.rb
index 855748fdccc3f708f9bf1bb9c81b762d76aa0d40..6ab04a584b0c8e8166a9e5cc908b5ac7e6140897 100644
--- a/lib/gitlab/markdown/cross_project_reference.rb
+++ b/lib/gitlab/markdown/cross_project_reference.rb
@@ -13,18 +13,11 @@ module Gitlab
       #
       # ref - String reference.
       #
-      # Returns a Project, or nil if the reference can't be accessed
+      # Returns a Project, or nil if the reference can't be found
       def project_from_ref(ref)
         return context[:project] unless ref
 
-        other = Project.find_with_namespace(ref)
-        return nil unless other && user_can_reference_project?(other)
-
-        other
-      end
-
-      def user_can_reference_project?(project, user = context[:current_user])
-        Ability.abilities.allowed?(user, :read_project, project)
+        Project.find_with_namespace(ref)
       end
     end
   end
diff --git a/lib/gitlab/markdown/external_issue_reference_filter.rb b/lib/gitlab/markdown/external_issue_reference_filter.rb
index f7c43e1ca89a3da59e500275830033baaa5bfbb5..8f86f13976abc847adb81542675fb4177a970c1d 100644
--- a/lib/gitlab/markdown/external_issue_reference_filter.rb
+++ b/lib/gitlab/markdown/external_issue_reference_filter.rb
@@ -47,8 +47,9 @@ module Gitlab
 
           title = escape_once("Issue in #{project.external_issue_tracker.title}")
           klass = reference_class(:issue)
+          data  = data_attribute(project: project.id)
 
-          %(<a href="#{url}"
+          %(<a href="#{url}" #{data}
                title="#{title}"
                class="#{klass}">#{match}</a>)
         end
diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb
index 01320f807963b2d4f1b730529174232061ff9825..481d282f7b1c6cb30609b4d6a82f7b7379ab55fc 100644
--- a/lib/gitlab/markdown/issue_reference_filter.rb
+++ b/lib/gitlab/markdown/issue_reference_filter.rb
@@ -27,6 +27,10 @@ module Gitlab
         end
       end
 
+      def self.referenced_by(node)
+        { issue: LazyReference.new(Issue, node.attr("data-issue")) }
+      end
+
       def call
         replace_text_nodes_matching(Issue.reference_pattern) do |content|
           issue_link_filter(content)
@@ -45,13 +49,11 @@ module Gitlab
           project = self.project_from_ref(project_ref)
 
           if project && issue = project.get_issue(id)
-            push_result(:issue, issue)
-
             url = url_for_issue(id, project, only_path: context[:only_path])
 
             title = escape_once("Issue: #{issue.title}")
             klass = reference_class(:issue)
-            data  = data_attribute(project.id)
+            data  = data_attribute(project: project.id, issue: issue.id)
 
             %(<a href="#{url}" #{data}
                  title="#{title}"
diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb
index 1e5cb12071eff475e8f3f1ad78f1380fff1eed03..618acb7a57814c32c1bc31f131a2d6cde8fdb40d 100644
--- a/lib/gitlab/markdown/label_reference_filter.rb
+++ b/lib/gitlab/markdown/label_reference_filter.rb
@@ -22,6 +22,10 @@ module Gitlab
         end
       end
 
+      def self.referenced_by(node)
+        { label: LazyReference.new(Label, node.attr("data-label")) }
+      end
+
       def call
         replace_text_nodes_matching(Label.reference_pattern) do |content|
           label_link_filter(content)
@@ -41,11 +45,9 @@ module Gitlab
           params = label_params(id, name)
 
           if label = project.labels.find_by(params)
-            push_result(:label, label)
-
             url = url_for_label(project, label)
             klass = reference_class(:label)
-            data = data_attribute(project.id)
+            data = data_attribute(project: project.id, label: label.id)
 
             %(<a href="#{url}" #{data}
                  class="#{klass}">#{render_colored_label(label)}</a>)
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index ecbd263d0e01a3863d19c3edbc1d3a8fc177eedc..5bc632698082556ec3132314a941fc14e12a924a 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -27,6 +27,10 @@ module Gitlab
         end
       end
 
+      def self.referenced_by(node)
+        { merge_request: LazyReference.new(MergeRequest, node.attr("data-merge-request")) }
+      end
+
       def call
         replace_text_nodes_matching(MergeRequest.reference_pattern) do |content|
           merge_request_link_filter(content)
@@ -45,11 +49,9 @@ module Gitlab
           project = self.project_from_ref(project_ref)
 
           if project && merge_request = project.merge_requests.find_by(iid: id)
-            push_result(:merge_request, merge_request)
-
             title = escape_once("Merge Request: #{merge_request.title}")
             klass = reference_class(:merge_request)
-            data  = data_attribute(project.id)
+            data  = data_attribute(project: project.id, merge_request: merge_request.id)
 
             url = url_for_merge_request(merge_request, project)
 
diff --git a/lib/gitlab/markdown/redactor_filter.rb b/lib/gitlab/markdown/redactor_filter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a1f3a8a8ebfc180a02fcf64b437947f9f6426e07
--- /dev/null
+++ b/lib/gitlab/markdown/redactor_filter.rb
@@ -0,0 +1,40 @@
+require 'gitlab/markdown'
+require 'html/pipeline/filter'
+
+module Gitlab
+  module Markdown
+    # HTML filter that removes references to records that the current user does
+    # not have permission to view.
+    #
+    # Expected to be run in its own post-processing pipeline.
+    #
+    class RedactorFilter < HTML::Pipeline::Filter
+      def call
+        doc.css('a.gfm').each do |node|
+          unless user_can_reference?(node)
+            node.replace(node.text)
+          end
+        end
+
+        doc
+      end
+
+      private
+
+      def user_can_reference?(node)
+        if node.has_attribute?('data-reference-filter')
+          reference_type = node.attr('data-reference-filter')
+          reference_filter = reference_type.constantize
+
+          reference_filter.user_can_reference?(current_user, node, context)
+        else
+          true
+        end
+      end
+
+      def current_user
+        context[:current_user]
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb
index 9b293c957d6807ad6c18b22fb89c9f4b125e797a..a4c560f578cae024b180fb3d05c0f0dd6e85f215 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/gitlab/markdown/reference_filter.rb
@@ -11,30 +11,57 @@ module Gitlab
     # Context options:
     #   :project (required) - Current project, ignored if reference is cross-project.
     #   :only_path          - Generate path-only links.
-    #
-    # Results:
-    #   :references - A Hash of references that were found and replaced.
     class ReferenceFilter < HTML::Pipeline::Filter
-      def initialize(*args)
-        super
+      LazyReference = Struct.new(:klass, :ids) do
+        def self.load(refs)
+          lazy_references, values = refs.partition { |ref| ref.is_a?(self) }
+
+          lazy_values = lazy_references.group_by(&:klass).flat_map do |klass, refs|
+            ids = refs.flat_map(&:ids)
+            klass.where(id: ids)
+          end
+
+          values + lazy_values
+        end
+
+        def load
+          self.klass.where(id: self.ids)
+        end
+      end
+
+      def self.user_can_reference?(user, node, context)
+        if node.has_attribute?('data-project')
+          project_id = node.attr('data-project').to_i
+          return true if project_id == context[:project].try(:id)
+
+          project = Project.find(project_id) rescue nil
+          Ability.abilities.allowed?(user, :read_project, project)
+        else
+          true
+        end
+      end
 
-        result[:references] = Hash.new { |hash, type| hash[type] = [] }
+      def self.referenced_by(node)
+        raise NotImplementedError, "#{self} does not implement #{__method__}"
       end
 
       # Returns a data attribute String to attach to a reference link
       #
-      # id   - Object ID
-      # type - Object type (default: :project)
+      # attributes - Hash, where the key becomes the data attribute name and the
+      #              value is the data attribute value
       #
       # Examples:
       #
-      #   data_attribute(1)         # => "data-project-id=\"1\""
-      #   data_attribute(2, :user)  # => "data-user-id=\"2\""
-      #   data_attribute(3, :group) # => "data-group-id=\"3\""
+      #   data_attribute(project: 1, issue: 2)
+      #   # => "data-reference-filter=\"Gitlab::Markdown::SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\""
+      #
+      #   data_attribute(project: 3, merge_request: 4)
+      #   # => "data-reference-filter=\"Gitlab::Markdown::SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\""
       #
       # Returns a String
-      def data_attribute(id, type = :project)
-        %Q(data-#{type}-id="#{id}")
+      def data_attribute(attributes = {})
+        attributes[:reference_filter] = self.class.name
+        attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{value}") }.join(" ")
       end
 
       def escape_once(html)
@@ -59,16 +86,6 @@ module Gitlab
         context[:project]
       end
 
-      # Add a reference to the pipeline's result Hash
-      #
-      # type   - Singular Symbol reference type (e.g., :issue, :user, etc.)
-      # values - One or more Objects to add
-      def push_result(type, *values)
-        return if values.empty?
-
-        result[:references][type].push(*values)
-      end
-
       def reference_class(type)
         "gfm gfm-#{type}"
       end
@@ -85,15 +102,15 @@ module Gitlab
       # Yields the current node's String contents. The result of the block will
       # replace the node's existing content and update the current document.
       #
-      # Returns the updated Nokogiri::XML::Document object.
+      # Returns the updated Nokogiri::HTML::DocumentFragment object.
       def replace_text_nodes_matching(pattern)
         return doc if project.nil?
 
         search_text_nodes(doc).each do |node|
-          content = node.to_html
-
-          next unless content.match(pattern)
           next if ignored_ancestry?(node)
+          next unless node.text =~ pattern
+
+          content = node.to_html
 
           html = yield content
 
diff --git a/lib/gitlab/markdown/reference_gatherer_filter.rb b/lib/gitlab/markdown/reference_gatherer_filter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..00f983675e6b90ba15b344e4ec56bd4ffc61c606
--- /dev/null
+++ b/lib/gitlab/markdown/reference_gatherer_filter.rb
@@ -0,0 +1,63 @@
+require 'gitlab/markdown'
+require 'html/pipeline/filter'
+
+module Gitlab
+  module Markdown
+    # HTML filter that gathers all referenced records that the current user has
+    # permission to view.
+    #
+    # Expected to be run in its own post-processing pipeline.
+    #
+    class ReferenceGathererFilter < HTML::Pipeline::Filter
+      def initialize(*)
+        super
+
+        result[:references] ||= Hash.new { |hash, type| hash[type] = [] }
+      end
+
+      def call
+        doc.css('a.gfm').each do |node|
+          gather_references(node)
+        end
+
+        load_lazy_references unless context[:load_lazy_references] == false
+
+        doc
+      end
+
+      private
+
+      def gather_references(node)
+        return unless node.has_attribute?('data-reference-filter')
+
+        reference_type = node.attr('data-reference-filter')
+        reference_filter = reference_type.constantize
+
+        return if context[:reference_filter] && reference_filter != context[:reference_filter]
+
+        return unless reference_filter.user_can_reference?(current_user, node, context)
+
+        references = reference_filter.referenced_by(node)
+        return unless references
+
+        references.each do |type, values|
+          Array.wrap(values).each do |value|
+            result[:references][type] << value
+          end
+        end
+      end
+
+      # Will load all references of one type using one query.
+      def load_lazy_references
+        refs = result[:references]
+        refs.each do |type, values|
+          refs[type] = ReferenceFilter::LazyReference.load(values)
+        end
+      end
+
+      def current_user
+        context[:current_user]
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb
index e368de7d8484866e6bc6a5d7d9ee0f7565ba81ac..ffb9dc33b641d7109dae0456272d60c3e853346c 100644
--- a/lib/gitlab/markdown/sanitization_filter.rb
+++ b/lib/gitlab/markdown/sanitization_filter.rb
@@ -48,6 +48,12 @@ module Gitlab
         # Allow span elements
         whitelist[:elements].push('span')
 
+        # Allow any protocol in `a` elements...
+        whitelist[:protocols].delete('a')
+
+        # ...but then remove links with the `javascript` protocol
+        whitelist[:transformers].push(remove_javascript_links)
+
         # Remove `rel` attribute from `a` elements
         whitelist[:transformers].push(remove_rel)
 
@@ -57,6 +63,19 @@ module Gitlab
         whitelist
       end
 
+      def remove_javascript_links
+        lambda do |env|
+          node = env[:node]
+
+          return unless node.name == 'a'
+          return unless node.has_attribute?('href')
+
+          if node['href'].start_with?('javascript', ':javascript')
+            node.remove_attribute('href')
+          end
+        end
+      end
+
       def remove_rel
         lambda do |env|
           if env[:node_name] == 'a'
diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb
index e2cf89cb1d853d7df94586f10f807cb5fca8b43e..f783f951711d974f656f2c3bebad63bb131034d1 100644
--- a/lib/gitlab/markdown/snippet_reference_filter.rb
+++ b/lib/gitlab/markdown/snippet_reference_filter.rb
@@ -27,6 +27,10 @@ module Gitlab
         end
       end
 
+      def self.referenced_by(node)
+        { snippet: LazyReference.new(Snippet, node.attr("data-snippet")) }
+      end
+
       def call
         replace_text_nodes_matching(Snippet.reference_pattern) do |content|
           snippet_link_filter(content)
@@ -45,11 +49,9 @@ module Gitlab
           project = self.project_from_ref(project_ref)
 
           if project && snippet = project.snippets.find_by(id: id)
-            push_result(:snippet, snippet)
-
             title = escape_once("Snippet: #{snippet.title}")
             klass = reference_class(:snippet)
-            data  = data_attribute(project.id)
+            data  = data_attribute(project: project.id, snippet: snippet.id)
 
             url = url_for_snippet(snippet, project)
 
diff --git a/lib/gitlab/markdown/upload_link_filter.rb b/lib/gitlab/markdown/upload_link_filter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fbada73ab865c9a677863164f8a1b814839fdf13
--- /dev/null
+++ b/lib/gitlab/markdown/upload_link_filter.rb
@@ -0,0 +1,47 @@
+require 'gitlab/markdown'
+require 'html/pipeline/filter'
+require 'uri'
+
+module Gitlab
+  module Markdown
+    # HTML filter that "fixes" relative upload links to files.
+    # Context options:
+    #   :project (required) - Current project
+    #
+    class UploadLinkFilter < HTML::Pipeline::Filter
+      def call
+        doc.search('a').each do |el|
+          process_link_attr el.attribute('href')
+        end
+
+        doc.search('img').each do |el|
+          process_link_attr el.attribute('src')
+        end
+
+        doc
+      end
+
+      protected
+
+      def process_link_attr(html_attr)
+        return if html_attr.blank?
+
+        uri = html_attr.value
+        if uri.starts_with?("/uploads/")
+          html_attr.value = build_url(uri).to_s
+        end
+      end
+
+      def build_url(uri)
+        File.join(Gitlab.config.gitlab.url, context[:project].path_with_namespace, uri)
+      end
+
+      # Ensure that a :project key exists in context
+      #
+      # Note that while the key might exist, its value could be nil!
+      def validate
+        needs :project
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb
index 6f436ea71675f9256057bfe259f0e578e281d117..2a594e1662ed9f2e9d00b01542ecafeb58e49ee4 100644
--- a/lib/gitlab/markdown/user_reference_filter.rb
+++ b/lib/gitlab/markdown/user_reference_filter.rb
@@ -23,6 +23,31 @@ module Gitlab
         end
       end
 
+      def self.referenced_by(node)
+        if node.has_attribute?('data-group')
+          group = Group.find(node.attr('data-group')) rescue nil
+          return unless group
+
+          { user: group.users }
+        elsif node.has_attribute?('data-user')
+          { user: LazyReference.new(User, node.attr('data-user')) }
+        elsif node.has_attribute?('data-project')
+          project = Project.find(node.attr('data-project')) rescue nil
+          return unless project
+
+          { user: project.team.members.flatten }
+        end
+      end
+
+      def self.user_can_reference?(user, node, context)
+        if node.has_attribute?('data-group')
+          group = Group.find(node.attr('data-group')) rescue nil
+          Ability.abilities.allowed?(user, :read_group, group)
+        else
+          super
+        end
+      end
+
       def call
         replace_text_nodes_matching(User.reference_pattern) do |content|
           user_link_filter(content)
@@ -61,14 +86,12 @@ module Gitlab
       def link_to_all
         project = context[:project]
 
-        # FIXME (rspeicher): Law of Demeter
-        push_result(:user, *project.team.members.flatten)
-
         url = urls.namespace_project_url(project.namespace, project,
                                          only_path: context[:only_path])
+        data = data_attribute(project: project.id)
 
         text = User.reference_prefix + 'all'
-        %(<a href="#{url}" class="#{link_class}">#{text}</a>)
+        %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
       end
 
       def link_to_namespace(namespace)
@@ -80,30 +103,20 @@ module Gitlab
       end
 
       def link_to_group(group, namespace)
-        return unless user_can_reference_group?(namespace)
-
-        push_result(:user, *namespace.users)
-
         url = urls.group_url(group, only_path: context[:only_path])
-        data = data_attribute(namespace.id, :group)
+        data = data_attribute(group: namespace.id)
 
         text = Group.reference_prefix + group
         %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
       end
 
       def link_to_user(user, namespace)
-        push_result(:user, namespace.owner)
-
         url = urls.user_url(user, only_path: context[:only_path])
-        data = data_attribute(namespace.owner_id, :user)
+        data = data_attribute(user: namespace.owner_id)
 
         text = User.reference_prefix + user
         %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
       end
-
-      def user_can_reference_group?(group)
-        Ability.abilities.allowed?(context[:current_user], :read_group, group)
-      end
     end
   end
 end
diff --git a/lib/gitlab/o_auth/provider.rb b/lib/gitlab/o_auth/provider.rb
index 90c3fe8da3339d10139e8a5a88eaacc633cf8eaf..9ad7a38d505919e971da85a2bbf58ebbcd1dedbc 100644
--- a/lib/gitlab/o_auth/provider.rb
+++ b/lib/gitlab/o_auth/provider.rb
@@ -1,6 +1,12 @@
 module Gitlab
   module OAuth
     class Provider
+      LABELS = {
+        "github"         => "GitHub",
+        "gitlab"         => "GitLab.com",
+        "google_oauth2"  => "Google"
+      }.freeze
+
       def self.providers
         Devise.omniauth_providers
       end
@@ -23,8 +29,9 @@ module Gitlab
       end
 
       def self.label_for(name)
+        name = name.to_s
         config = config_for(name)
-        (config && config['label']) || name.to_s.titleize
+        (config && config['label']) || LABELS[name] || name.titleize
       end
     end
   end
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 0dab7bcfa4d0c9af1081042fdd3121bdb924b779..70de6a74e767a928fde2549f9582f22135642bdf 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -9,7 +9,7 @@ module Gitlab
                         else
                           nil
                         end
-      @query = Shellwords.shellescape(query) if query.present?
+      @query = query
     end
 
     def objects(scope, page = nil)
@@ -20,6 +20,8 @@ module Gitlab
         Kaminari.paginate_array(blobs).page(page).per(per_page)
       when 'wiki_blobs'
         Kaminari.paginate_array(wiki_blobs).page(page).per(per_page)
+      when 'commits'
+        Kaminari.paginate_array(commits).page(page).per(per_page)
       else
         super
       end
@@ -27,7 +29,7 @@ module Gitlab
 
     def total_count
       @total_count ||= issues_count + merge_requests_count + blobs_count +
-                       notes_count + wiki_blobs_count
+                       notes_count + wiki_blobs_count + commits_count
     end
 
     def blobs_count
@@ -42,6 +44,10 @@ module Gitlab
       @wiki_blobs_count ||= wiki_blobs.count
     end
 
+    def commits_count
+      @commits_count ||= commits.count
+    end
+
     private
 
     def blobs
@@ -70,6 +76,14 @@ module Gitlab
       Note.where(project_id: limit_project_ids).user.search(query).order('updated_at DESC')
     end
 
+    def commits
+      if project.empty_repo? || query.blank?
+        []
+      else
+        project.repository.find_commits_by_message(query).compact
+      end
+    end
+
     def limit_project_ids
       [project.id]
     end
diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb
index d010ade704e0eb7a6e4de974e2548a20c38bc2ce..fa068d507634a1351985bab5e2877dfd8484e1e3 100644
--- a/lib/gitlab/push_data_builder.rb
+++ b/lib/gitlab/push_data_builder.rb
@@ -18,7 +18,10 @@ module Gitlab
       #     homepage: String,
       #   },
       #   commits: Array,
-      #   total_commits_count: Fixnum
+      #   total_commits_count: Fixnum,
+      #   added: ["CHANGELOG"],
+      #   modified: [],
+      #   removed: ["tmp/file.txt"]
       # }
       #
       def build(project, user, oldrev, newrev, ref, commits = [], message = nil)
@@ -33,6 +36,8 @@ module Gitlab
         commit_attrs = commits_limited.map(&:hook_attrs)
 
         type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push"
+
+        repo_changes = repo_changes(project, newrev, oldrev)
         # Hash to be passed as post_receive_data
         data = {
           object_kind: type,
@@ -55,7 +60,10 @@ module Gitlab
             visibility_level: project.visibility_level
           },
           commits: commit_attrs,
-          total_commits_count: commits_count
+          total_commits_count: commits_count,
+          added: repo_changes[:added],
+          modified: repo_changes[:modified],
+          removed: repo_changes[:removed]
         }
 
         data
@@ -86,6 +94,27 @@ module Gitlab
           newrev
         end
       end
+
+      def repo_changes(project, newrev, oldrev)
+        changes = { added: [], modified: [], removed: [] }
+        compare_result = CompareService.new.
+          execute(project, newrev, project, oldrev)
+
+        if compare_result
+          compare_result.diffs.each do |diff|
+            case true
+            when diff.deleted_file
+              changes[:removed] << diff.old_path
+            when diff.renamed_file, diff.new_file
+              changes[:added] << diff.new_path
+            else
+              changes[:modified] << diff.new_path
+            end
+          end
+        end
+
+        changes
+      end
     end
   end
 end
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 0961bd80421a966bdc575fcce10eaa055ce6ecfe..da8df8a3025550e5a23d73c19c303fd0894464ef 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -3,11 +3,12 @@ require 'gitlab/markdown'
 module Gitlab
   # Extract possible GFM references from an arbitrary String for further processing.
   class ReferenceExtractor
-    attr_accessor :project, :current_user
+    attr_accessor :project, :current_user, :load_lazy_references
 
-    def initialize(project, current_user = nil)
+    def initialize(project, current_user = nil, load_lazy_references: true)
       @project = project
       @current_user = current_user
+      @load_lazy_references = load_lazy_references
     end
 
     def analyze(text)
@@ -26,9 +27,9 @@ module Gitlab
     def references
       @references ||= Hash.new do |references, type|
         type = type.to_sym
-        return references[type] if references.has_key?(type)
+        next references[type] if references.has_key?(type)
 
-        references[type] = pipeline_result(type).uniq
+        references[type] = pipeline_result(type)
       end
     end
 
@@ -39,21 +40,34 @@ module Gitlab
     #
     # Returns the results Array for the requested filter type
     def pipeline_result(filter_type)
-      klass  = filter_type.to_s.camelize + 'ReferenceFilter'
+      return [] if @text.blank?
+      
+      klass  = "#{filter_type.to_s.camelize}ReferenceFilter"
       filter = Gitlab::Markdown.const_get(klass)
 
       context = {
         project: project,
         current_user: current_user,
+        
         # We don't actually care about the links generated
         only_path: true,
-        ignore_blockquotes: true
+        ignore_blockquotes: true,
+
+        # ReferenceGathererFilter
+        load_lazy_references: false,
+        reference_filter:     filter
       }
 
-      pipeline = HTML::Pipeline.new([filter], context)
+      pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context)
       result = pipeline.call(@text)
 
-      result[:references][filter_type]
+      values = result[:references][filter_type].uniq
+
+      if @load_lazy_references
+        values = Gitlab::Markdown::ReferenceFilter::LazyReference.load(values).uniq
+      end
+
+      values
     end
   end
 end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 9f1adc860d1d15c866b18eca9179fda852788dfd..53ab2686b43e0c4202a1fc51aedd6ba14a2b9228 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -51,6 +51,23 @@ module Gitlab
       "can contain only letters, digits, '_', '-' and '.'. "
     end
 
+    def file_path_regex
+      @file_path_regex ||= /\A[a-zA-Z0-9_\-\.\/]*\z/.freeze
+    end
+
+    def file_path_regex_message
+      "can contain only letters, digits, '_', '-' and '.'. Separate directories with a '/'. "
+    end
+
+
+    def directory_traversal_regex
+      @directory_traversal_regex ||= /\.{2}/.freeze
+    end
+
+    def directory_traversal_regex_message
+      "cannot include directory traversal. "
+    end
+
 
     def archive_formats_regex
       #                           |zip|tar|    tar.gz    |         tar.bz2         |
diff --git a/lib/gitlab/sherlock.rb b/lib/gitlab/sherlock.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6360527a7aa1db2b2175cb657f4ae39848421074
--- /dev/null
+++ b/lib/gitlab/sherlock.rb
@@ -0,0 +1,19 @@
+require 'securerandom'
+
+module Gitlab
+  module Sherlock
+    @collection = Collection.new
+
+    class << self
+      attr_reader :collection
+    end
+
+    def self.enabled?
+      Rails.env.development? && !!ENV['ENABLE_SHERLOCK']
+    end
+
+    def self.enable_line_profiler?
+      RUBY_ENGINE == 'ruby'
+    end
+  end
+end
diff --git a/lib/gitlab/sherlock/collection.rb b/lib/gitlab/sherlock/collection.rb
new file mode 100644
index 0000000000000000000000000000000000000000..66bd6258521deedc0e6781802c161868c87819ae
--- /dev/null
+++ b/lib/gitlab/sherlock/collection.rb
@@ -0,0 +1,49 @@
+module Gitlab
+  module Sherlock
+    # A collection of transactions recorded by Sherlock.
+    #
+    # Method calls for this class are synchronized using a mutex to allow
+    # sharing of a single Collection instance between threads (e.g. when using
+    # Puma as a webserver).
+    class Collection
+      include Enumerable
+
+      def initialize
+        @transactions = []
+        @mutex = Mutex.new
+      end
+
+      def add(transaction)
+        synchronize { @transactions << transaction }
+      end
+
+      alias_method :<<, :add
+
+      def each(&block)
+        synchronize { @transactions.each(&block) }
+      end
+
+      def clear
+        synchronize { @transactions.clear }
+      end
+
+      def empty?
+        synchronize { @transactions.empty? }
+      end
+
+      def find_transaction(id)
+        find { |trans| trans.id == id }
+      end
+
+      def newest_first
+        sort { |a, b| b.finished_at <=> a.finished_at }
+      end
+
+      private
+
+      def synchronize(&block)
+        @mutex.synchronize(&block)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/sherlock/file_sample.rb b/lib/gitlab/sherlock/file_sample.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8a3e1a5e5bf1747ae6c62d4f9880556309a3f549
--- /dev/null
+++ b/lib/gitlab/sherlock/file_sample.rb
@@ -0,0 +1,31 @@
+module Gitlab
+  module Sherlock
+    class FileSample
+      attr_reader :id, :file, :line_samples, :events, :duration
+
+      # file - The full path to the file this sample belongs to.
+      # line_samples - An array of LineSample objects.
+      # duration - The total execution time in milliseconds.
+      # events - The total amount of events.
+      def initialize(file, line_samples, duration, events)
+        @id = SecureRandom.uuid
+        @file = file
+        @line_samples = line_samples
+        @duration = duration
+        @events = events
+      end
+
+      def relative_path
+        @relative_path ||= @file.gsub(/^#{Rails.root.to_s}\/?/, '')
+      end
+
+      def to_param
+        @id
+      end
+
+      def source
+        @source ||= File.read(@file)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/sherlock/line_profiler.rb b/lib/gitlab/sherlock/line_profiler.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aa1468bff6bc66de1af5f493bd2fd887630c6578
--- /dev/null
+++ b/lib/gitlab/sherlock/line_profiler.rb
@@ -0,0 +1,98 @@
+module Gitlab
+  module Sherlock
+    # Class for profiling code on a per line basis.
+    #
+    # The LineProfiler class can be used to profile code on per line basis
+    # without littering your code with Ruby implementation specific profiling
+    # methods.
+    #
+    # This profiler only includes samples taking longer than a given threshold
+    # and those that occur in the actual application (e.g. files from Gems are
+    # ignored).
+    class LineProfiler
+      # The minimum amount of time that has to be spent in a file for it to be
+      # included in a list of samples.
+      MINIMUM_DURATION = 10.0
+
+      # Profiles the given block.
+      #
+      # Example:
+      #
+      #     profiler = LineProfiler.new
+      #
+      #     retval, samples = profiler.profile do
+      #       "cats are amazing"
+      #     end
+      #
+      #     retval  # => "cats are amazing"
+      #     samples # => [#<Gitlab::Sherlock::FileSample ...>, ...]
+      #
+      # Returns an Array containing the block's return value and an Array of
+      # FileSample objects.
+      def profile(&block)
+        if mri?
+          profile_mri(&block)
+        else
+          raise NotImplementedError,
+            'Line profiling is not supported on this platform'
+        end
+      end
+
+      # Profiles the given block using rblineprof (MRI only).
+      def profile_mri
+        require 'rblineprof'
+
+        retval  = nil
+        samples = lineprof(/^#{Rails.root.to_s}/) { retval = yield }
+
+        file_samples = aggregate_rblineprof(samples)
+
+        [retval, file_samples]
+      end
+
+      # Returns an Array of file samples based on the output of rblineprof.
+      #
+      # lineprof_stats - A Hash containing rblineprof statistics on a per file
+      #                  basis.
+      #
+      # Returns an Array of FileSample objects.
+      def aggregate_rblineprof(lineprof_stats)
+        samples = []
+
+        lineprof_stats.each do |(file, stats)|
+          source_lines = File.read(file).each_line.to_a
+          line_samples = []
+
+          total_duration = microsec_to_millisec(stats[0][0])
+          total_events   = stats[0][2]
+
+          next if total_duration <= MINIMUM_DURATION
+
+          stats[1..-1].each_with_index do |data, index|
+            next unless source_lines[index]
+
+            duration = microsec_to_millisec(data[0])
+            events   = data[2]
+
+            line_samples << LineSample.new(duration, events)
+          end
+
+          samples << FileSample.
+            new(file, line_samples, total_duration, total_events)
+        end
+
+        samples
+      end
+
+      private
+
+      def microsec_to_millisec(microsec)
+        microsec / 1000.0
+      end
+
+      def mri?
+        RUBY_ENGINE == 'ruby'
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/sherlock/line_sample.rb b/lib/gitlab/sherlock/line_sample.rb
new file mode 100644
index 0000000000000000000000000000000000000000..eb1948eb6d6ff1ee5b818e49837da707551a87de
--- /dev/null
+++ b/lib/gitlab/sherlock/line_sample.rb
@@ -0,0 +1,36 @@
+module Gitlab
+  module Sherlock
+    class LineSample
+      attr_reader :duration, :events
+
+      # duration - The execution time in milliseconds.
+      # events - The amount of events.
+      def initialize(duration, events)
+        @duration = duration
+        @events = events
+      end
+
+      # Returns the sample duration percentage relative to the given duration.
+      #
+      # Example:
+      #
+      #     sample.duration            # => 150
+      #     sample.percentage_of(1500) # => 10.0
+      #
+      # total_duration - The total duration to compare with.
+      #
+      # Returns a float
+      def percentage_of(total_duration)
+        (duration.to_f / total_duration) * 100.0
+      end
+
+      # Returns true if the current sample takes up the majority of the given
+      # duration.
+      #
+      # total_duration - The total duration to compare with.
+      def majority_of?(total_duration)
+        percentage_of(total_duration) >= 30
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/sherlock/location.rb b/lib/gitlab/sherlock/location.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5ac265618ad2175eeeaee026506b6c6695fa7083
--- /dev/null
+++ b/lib/gitlab/sherlock/location.rb
@@ -0,0 +1,26 @@
+module Gitlab
+  module Sherlock
+    class Location
+      attr_reader :path, :line
+
+      SHERLOCK_DIR = File.dirname(__FILE__)
+
+      # Creates a new Location from a `Thread::Backtrace::Location`.
+      def self.from_ruby_location(location)
+        new(location.path, location.lineno)
+      end
+
+      # path - The full path of the frame as a String.
+      # line - The line number of the frame as a Fixnum.
+      def initialize(path, line)
+        @path = path
+        @line = line
+      end
+
+      # Returns true if the current frame originated from the application.
+      def application?
+        @path.start_with?(Rails.root.to_s) && !path.start_with?(SHERLOCK_DIR)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/sherlock/middleware.rb b/lib/gitlab/sherlock/middleware.rb
new file mode 100644
index 0000000000000000000000000000000000000000..687332fc5fc18fb45bd408e84ebe038bc7eb3c90
--- /dev/null
+++ b/lib/gitlab/sherlock/middleware.rb
@@ -0,0 +1,41 @@
+module Gitlab
+  module Sherlock
+    # Rack middleware used for tracking request metrics.
+    class Middleware
+      CONTENT_TYPES = /text\/html|application\/json/i
+
+      IGNORE_PATHS = %r{^/sherlock}
+
+      def initialize(app)
+        @app = app
+      end
+
+      # env - A Hash containing Rack environment details.
+      def call(env)
+        if instrument?(env)
+          call_with_instrumentation(env)
+        else
+          @app.call(env)
+        end
+      end
+
+      def call_with_instrumentation(env)
+        trans = transaction_from_env(env)
+        retval = trans.run { @app.call(env) }
+
+        Sherlock.collection.add(trans)
+
+        retval
+      end
+
+      def instrument?(env)
+        !!(env['HTTP_ACCEPT'] =~ CONTENT_TYPES &&
+           env['REQUEST_URI'] !~ IGNORE_PATHS)
+      end
+
+      def transaction_from_env(env)
+        Transaction.new(env['REQUEST_METHOD'], env['REQUEST_URI'])
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/sherlock/query.rb b/lib/gitlab/sherlock/query.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4917c4ae2ac5c1a0a3b95b72d8cd2a25d42709db
--- /dev/null
+++ b/lib/gitlab/sherlock/query.rb
@@ -0,0 +1,114 @@
+module Gitlab
+  module Sherlock
+    class Query
+      attr_reader :id, :query, :started_at, :finished_at, :backtrace
+
+      # SQL identifiers that should be prefixed with newlines.
+      PREFIX_NEWLINE = /
+        \s+(FROM
+          |(LEFT|RIGHT)?INNER\s+JOIN
+          |(LEFT|RIGHT)?OUTER\s+JOIN
+          |WHERE
+          |AND
+          |GROUP\s+BY
+          |ORDER\s+BY
+          |LIMIT
+          |OFFSET)\s+/ix # Vim indent breaks when this is on a newline :<
+
+      # Creates a new Query using a String and a separate Array of bindings.
+      #
+      # query - A String containing a SQL query, optionally with numeric
+      #         placeholders (`$1`, `$2`, etc).
+      #
+      # bindings - An Array of ActiveRecord columns and their values.
+      # started_at - The start time of the query as a Time-like object.
+      # finished_at - The completion time of the query as a Time-like object.
+      #
+      # Returns a new Query object.
+      def self.new_with_bindings(query, bindings, started_at, finished_at)
+        bindings.each_with_index do |(_, value), index|
+          quoted_value = ActiveRecord::Base.connection.quote(value)
+
+          query = query.gsub("$#{index + 1}", quoted_value)
+        end
+
+        new(query, started_at, finished_at)
+      end
+
+      # query - The SQL query as a String (without placeholders).
+      # started_at - The start time of the query as a Time-like object.
+      # finished_at - The completion time of the query as a Time-like object.
+      def initialize(query, started_at, finished_at)
+        @id = SecureRandom.uuid
+        @query = query
+        @started_at = started_at
+        @finished_at = finished_at
+        @backtrace = caller_locations.map do |loc|
+          Location.from_ruby_location(loc)
+        end
+
+        unless @query.end_with?(';')
+          @query += ';'
+        end
+      end
+
+      # Returns the query duration in milliseconds.
+      def duration
+        @duration ||= (@finished_at - @started_at) * 1000.0
+      end
+
+      def to_param
+        @id
+      end
+
+      # Returns a human readable version of the query.
+      def formatted_query
+        @formatted_query ||= format_sql(@query)
+      end
+
+      # Returns the last application frame of the backtrace.
+      def last_application_frame
+        @last_application_frame ||= @backtrace.find(&:application?)
+      end
+
+      # Returns an Array of application frames (excluding Gems and the likes).
+      def application_backtrace
+        @application_backtrace ||= @backtrace.select(&:application?)
+      end
+
+      # Returns the query plan as a String.
+      def explain
+        unless @explain
+          ActiveRecord::Base.connection.transaction do
+            @explain = raw_explain(@query).values.flatten.join("\n")
+
+            # Roll back any queries that mutate data so we don't mess up
+            # anything when running explain on an INSERT, UPDATE, DELETE, etc.
+            raise ActiveRecord::Rollback
+          end
+        end
+
+        @explain
+      end
+
+      private
+
+      def raw_explain(query)
+        if Gitlab::Database.postgresql?
+          explain = "EXPLAIN ANALYZE #{query};"
+        else
+          explain = "EXPLAIN #{query};"
+        end
+
+        ActiveRecord::Base.connection.execute(explain)
+      end
+
+      def format_sql(query)
+        query.each_line.
+          map { |line| line.strip }.
+          join("\n").
+          gsub(PREFIX_NEWLINE) { "\n#{$1} " }
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d87a4c9bb4a82a5e59dc6dbe87dfe0c6436e6673
--- /dev/null
+++ b/lib/gitlab/sherlock/transaction.rb
@@ -0,0 +1,131 @@
+module Gitlab
+  module Sherlock
+    class Transaction
+      attr_reader :id, :type, :path, :queries, :file_samples, :started_at,
+        :finished_at, :view_counts
+
+      # type - The type of transaction (e.g. "GET", "POST", etc)
+      # path - The path of the transaction (e.g. the HTTP request path)
+      def initialize(type, path)
+        @id = SecureRandom.uuid
+        @type = type
+        @path = path
+        @queries = []
+        @file_samples = []
+        @started_at = nil
+        @finished_at = nil
+        @thread = Thread.current
+        @view_counts = Hash.new(0)
+      end
+
+      # Runs the transaction and returns the block's return value.
+      def run
+        @started_at = Time.now
+
+        retval = with_subscriptions do
+          profile_lines { yield }
+        end
+
+        @finished_at = Time.now
+
+        retval
+      end
+
+      # Returns the duration in seconds.
+      def duration
+        @duration ||= started_at && finished_at ? finished_at - started_at : 0
+      end
+
+      def to_param
+        @id
+      end
+
+      # Returns the queries sorted in descending order by their durations.
+      def sorted_queries
+        @queries.sort { |a, b| b.duration <=> a.duration }
+      end
+
+      # Returns the file samples sorted in descending order by their durations.
+      def sorted_file_samples
+        @file_samples.sort { |a, b| b.duration <=> a.duration }
+      end
+
+      # Finds a query by the given ID.
+      #
+      # id - The query ID as a String.
+      #
+      # Returns a Query object if one could be found, nil otherwise.
+      def find_query(id)
+        @queries.find { |query| query.id == id }
+      end
+
+      # Finds a file sample by the given ID.
+      #
+      # id - The query ID as a String.
+      #
+      # Returns a FileSample object if one could be found, nil otherwise.
+      def find_file_sample(id)
+        @file_samples.find { |sample| sample.id == id }
+      end
+
+      def profile_lines
+        retval = nil
+
+        if Sherlock.enable_line_profiler?
+          retval, @file_samples = LineProfiler.new.profile { yield }
+        else
+          retval = yield
+        end
+
+        retval
+      end
+
+      def subscribe_to_active_record
+        ActiveSupport::Notifications.subscribe('sql.active_record') do |_, start, finish, _, data|
+          next unless same_thread?
+
+          track_query(data[:sql].strip, data[:binds], start, finish)
+        end
+      end
+
+      def subscribe_to_action_view
+        regex = /render_(template|partial)\.action_view/
+
+        ActiveSupport::Notifications.subscribe(regex) do |_, start, finish, _, data|
+          next unless same_thread?
+
+          track_view(data[:identifier])
+        end
+      end
+
+      private
+
+      def track_query(query, bindings, start, finish)
+        @queries << Query.new_with_bindings(query, bindings, start, finish)
+      end
+
+      def track_view(path)
+        @view_counts[path] += 1
+      end
+
+      def with_subscriptions
+        ar_subscriber = subscribe_to_active_record
+        av_subscriber = subscribe_to_action_view
+
+        retval = yield
+
+        ActiveSupport::Notifications.unsubscribe(ar_subscriber)
+        ActiveSupport::Notifications.unsubscribe(av_subscriber)
+
+        retval
+      end
+
+      # In case somebody uses a multi-threaded server locally (e.g. Puma) we
+      # _only_ want to track notifications that originate from the transaction
+      # thread.
+      def same_thread?
+        Thread.current == @thread
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb
index cf040971c6eef009f47291ec33c42260799aa16d..f3567f3ef8505d4dd44219ab1f3e76cf7bd4a846 100644
--- a/lib/gitlab/upgrader.rb
+++ b/lib/gitlab/upgrader.rb
@@ -50,15 +50,15 @@ module Gitlab
     end
 
     def fetch_git_tags
-      remote_tags, _ = Gitlab::Popen.popen(%W(git ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git))
+      remote_tags, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git))
       remote_tags.split("\n").grep(/tags\/v#{current_version.major}/)
     end
 
     def update_commands
       {
-        "Stash changed files" => %W(git stash),
-        "Get latest code" => %W(git fetch),
-        "Switch to new version" => %W(git checkout v#{latest_version}),
+        "Stash changed files" => %W(#{Gitlab.config.git.bin_path} stash),
+        "Get latest code" => %W(#{Gitlab.config.git.bin_path} fetch),
+        "Switch to new version" => %W(#{Gitlab.config.git.bin_path} checkout v#{latest_version}),
         "Install gems" => %W(bundle),
         "Migrate DB" => %W(bundle exec rake db:migrate),
         "Recompile assets" => %W(bundle exec rake assets:clean assets:precompile),
diff --git a/lib/gitlab/uploads_transfer.rb b/lib/gitlab/uploads_transfer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..be8fcc7b2d256db48600707493f343e198ea4370
--- /dev/null
+++ b/lib/gitlab/uploads_transfer.rb
@@ -0,0 +1,35 @@
+module Gitlab
+  class UploadsTransfer
+    def move_project(project_path, namespace_path_was, namespace_path)
+      new_namespace_folder = File.join(root_dir, namespace_path)
+      FileUtils.mkdir_p(new_namespace_folder) unless Dir.exist?(new_namespace_folder)
+      from = File.join(root_dir, namespace_path_was, project_path)
+      to = File.join(root_dir, namespace_path, project_path)
+      move(from, to, "")
+    end
+
+    def rename_project(path_was, path, namespace_path)
+      base_dir = File.join(root_dir, namespace_path)
+      move(path_was, path, base_dir)
+    end
+
+    def rename_namespace(path_was, path)
+      move(path_was, path)
+    end
+
+    private
+
+    def move(path_was, path, base_dir = nil)
+      base_dir = root_dir unless base_dir
+      from = File.join(base_dir, path_was)
+      to = File.join(base_dir, path)
+      FileUtils.mv(from, to)
+    rescue Errno::ENOENT
+      false
+    end
+
+    def root_dir
+      File.join(Rails.root, "public", "uploads")
+    end
+  end
+end
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index a80e7e77430ce6823dacba99be5f6ccb6f738bff..f0a6c2b30e97ba2d82ac2c0a9dd9a38c1cc72a34 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -37,10 +37,9 @@ web_server_pid_path="$pid_path/unicorn.pid"
 sidekiq_pid_path="$pid_path/sidekiq.pid"
 mail_room_enabled=false
 mail_room_pid_path="$pid_path/mail_room.pid"
-gitlab_git_http_server_pid_path="$pid_path/gitlab-git-http-server.pid"
-gitlab_git_http_server_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-git-http-server.socket -authBackend http://127.0.0.1:8080"
-gitlab_git_http_server_repo_root='/home/git/repositories'
-gitlab_git_http_server_log="$app_root/log/gitlab-git-http-server.log"
+gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
+gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080"
+gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
 shell_path="/bin/bash"
 
 # Read configuration variable file if it is present
@@ -76,8 +75,8 @@ check_pids(){
   else
     spid=0
   fi
-  if [ -f "$gitlab_git_http_server_pid_path" ]; then
-    hpid=$(cat "$gitlab_git_http_server_pid_path")
+  if [ -f "$gitlab_workhorse_pid_path" ]; then
+    hpid=$(cat "$gitlab_workhorse_pid_path")
   else
     hpid=0
   fi
@@ -94,7 +93,7 @@ check_pids(){
 wait_for_pids(){
   # We are sleeping a bit here mostly because sidekiq is slow at writing it's pid
   i=0;
-  while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_git_http_server_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; }; do
+  while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; }; do
     sleep 0.1;
     i=$((i+1))
     if [ $((i%10)) = 0 ]; then
@@ -131,9 +130,9 @@ check_status(){
   fi
   if [ $hpid -ne 0 ]; then
     kill -0 "$hpid" 2>/dev/null
-    gitlab_git_http_server_status="$?"
+    gitlab_workhorse_status="$?"
   else
-    gitlab_git_http_server_status="-1"
+    gitlab_workhorse_status="-1"
   fi
   if [ "$mail_room_enabled" = true ]; then
     if [ $mpid -ne 0 ]; then
@@ -143,7 +142,7 @@ check_status(){
       mail_room_status="-1"
     fi
   fi
-  if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_git_http_server_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; }; then
+  if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; }; then
     gitlab_status=0
   else
     # http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
@@ -171,9 +170,9 @@ check_stale_pids(){
       exit 1
     fi
   fi
-  if [ "$hpid" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ]; then
-    echo "Removing stale gitlab-git-http-server pid. This is most likely caused by gitlab-git-http-server crashing the last time it ran."
-    if ! rm "$gitlab_git_http_server_pid_path"; then
+  if [ "$hpid" != "0" ] && [ "$gitlab_workhorse_status" != "0" ]; then
+    echo "Removing stale gitlab-workhorse pid. This is most likely caused by gitlab-workhorse crashing the last time it ran."
+    if ! rm "$gitlab_workhorse_pid_path"; then
       echo "Unable to remove stale pid, exiting"
       exit 1
     fi
@@ -190,7 +189,7 @@ check_stale_pids(){
 ## If no parts of the service is running, bail out.
 exit_if_not_running(){
   check_stale_pids
-  if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
+  if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
     echo "GitLab is not running."
     exit
   fi
@@ -206,8 +205,8 @@ start_gitlab() {
   if [ "$sidekiq_status" != "0" ]; then
     echo "Starting GitLab Sidekiq"
   fi
-  if [ "$gitlab_git_http_server_status" != "0" ]; then
-    echo "Starting gitlab-git-http-server"
+  if [ "$gitlab_workhorse_status" != "0" ]; then
+    echo "Starting gitlab-workhorse"
   fi
   if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" != "0" ]; then
     echo "Starting GitLab MailRoom"
@@ -230,15 +229,14 @@ start_gitlab() {
     RAILS_ENV=$RAILS_ENV bin/background_jobs start &
   fi
 
-  if [ "$gitlab_git_http_server_status" = "0" ]; then
-    echo "The gitlab-git-http-server is already running with pid $spid, not restarting"
+  if [ "$gitlab_workhorse_status" = "0" ]; then
+    echo "The gitlab-workhorse is already running with pid $spid, not restarting"
   else
-    # No need to remove a socket, gitlab-git-http-server does this itself
-    $app_root/bin/daemon_with_pidfile $gitlab_git_http_server_pid_path  \
-      $app_root/../gitlab-git-http-server/gitlab-git-http-server \
-        $gitlab_git_http_server_options \
-        $gitlab_git_http_server_repo_root \
-      >> $gitlab_git_http_server_log 2>&1 &
+    # No need to remove a socket, gitlab-workhorse does this itself
+    $app_root/bin/daemon_with_pidfile $gitlab_workhorse_pid_path  \
+      $app_root/../gitlab-workhorse/gitlab-workhorse \
+        $gitlab_workhorse_options \
+      >> $gitlab_workhorse_log 2>&1 &
   fi
 
   if [ "$mail_room_enabled" = true ]; then
@@ -268,9 +266,9 @@ stop_gitlab() {
     echo "Shutting down GitLab Sidekiq"
     RAILS_ENV=$RAILS_ENV bin/background_jobs stop
   fi
-  if [ "$gitlab_git_http_server_status" = "0" ]; then
-    echo "Shutting down gitlab-git-http-server"
-    kill -- $(cat $gitlab_git_http_server_pid_path)
+  if [ "$gitlab_workhorse_status" = "0" ]; then
+    echo "Shutting down gitlab-workhorse"
+    kill -- $(cat $gitlab_workhorse_pid_path)
   fi
   if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; then
     echo "Shutting down GitLab MailRoom"
@@ -278,11 +276,11 @@ stop_gitlab() {
   fi
 
   # If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script.
-  while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_git_http_server_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; do
+  while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; do
     sleep 1
     check_status
     printf "."
-    if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
+    if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
       printf "\n"
       break
     fi
@@ -292,7 +290,7 @@ stop_gitlab() {
   # Cleaning up unused pids
   rm "$web_server_pid_path" 2>/dev/null
   # rm "$sidekiq_pid_path" 2>/dev/null # Sidekiq seems to be cleaning up it's own pid.
-  rm -f "$gitlab_git_http_server_pid_path"
+  rm -f "$gitlab_workhorse_pid_path"
   if [ "$mail_room_enabled" = true ]; then
     rm "$mail_room_pid_path" 2>/dev/null
   fi
@@ -303,7 +301,7 @@ stop_gitlab() {
 ## Prints the status of GitLab and it's components.
 print_status() {
   check_status
-  if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
+  if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then
     echo "GitLab is not running."
     return
   fi
@@ -317,10 +315,10 @@ print_status() {
   else
       printf "The GitLab Sidekiq job dispatcher is \033[31mnot running\033[0m.\n"
   fi
-  if [ "$gitlab_git_http_server_status" = "0" ]; then
-      echo "The gitlab-git-http-server with pid $hpid is running."
+  if [ "$gitlab_workhorse_status" = "0" ]; then
+      echo "The gitlab-workhorse with pid $hpid is running."
   else
-      printf "The gitlab-git-http-server is \033[31mnot running\033[0m.\n"
+      printf "The gitlab-workhorse is \033[31mnot running\033[0m.\n"
   fi
   if [ "$mail_room_enabled" = true ]; then
     if [ "$mail_room_status" = "0" ]; then
@@ -360,7 +358,7 @@ reload_gitlab(){
 ## Restarts Sidekiq and Unicorn.
 restart_gitlab(){
   check_status
-  if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_git_http_server" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; then
+  if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; then
     stop_gitlab
   fi
   start_gitlab
diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
index aab5acaa72ccd1a39ecbc4ceb798cb51958c1012..79ae8e0ae55abf2fb3fe76dd692b1b4fe9dd3462 100755
--- a/lib/support/init.d/gitlab.default.example
+++ b/lib/support/init.d/gitlab.default.example
@@ -30,15 +30,14 @@ web_server_pid_path="$pid_path/unicorn.pid"
 # The default is "$pid_path/sidekiq.pid"
 sidekiq_pid_path="$pid_path/sidekiq.pid"
 
-gitlab_git_http_server_pid_path="$pid_path/gitlab-git-http-server.pid"
-# The -listenXxx settings determine where gitlab-git-http-server
+gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
+# The -listenXxx settings determine where gitlab-workhorse
 # listens for connections from NGINX. To listen on localhost:8181, write
 # '-listenNetwork tcp -listenAddr localhost:8181'.
-# The -authBackend setting tells gitlab-git-http-server where it can reach
+# The -authBackend setting tells gitlab-workhorse where it can reach
 # Unicorn.
-gitlab_git_http_server_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-git-http-server.socket -authBackend http://127.0.0.1:8080"
-gitlab_git_http_server_repo_root="/home/git/repositories"
-gitlab_git_http_server_log="$app_root/log/gitlab-git-http-server.log"
+gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080"
+gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
 
 # mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled.
 # This is required for the Reply by email feature.
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 7218a4d2f2092c57eb21da7d3a88f41c81df29ea..e767027dc29ad33c3ae1609e8201298c5f8b2b0f 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -38,8 +38,8 @@ upstream gitlab {
   server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
 }
 
-upstream gitlab-git-http-server {
-  server unix:/home/git/gitlab/tmp/sockets/gitlab-git-http-server.socket fail_timeout=0;
+upstream gitlab-workhorse {
+  server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
 }
 
 ## Normal HTTP host
@@ -113,7 +113,25 @@ server {
     proxy_pass http://gitlab;
   }
 
-  location ~ [-\/\w\.]+\.git\/ {
+  location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
+  location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive {
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
+  location ~ ^/api/v3/projects/.*/repository/archive {
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
+  location @gitlab-workhorse {
     ## If you use HTTPS make sure you disable gzip compression
     ## to be safe against BREACH attack.
     # gzip off;
@@ -129,7 +147,7 @@ server {
 
     # The following settings only work with NGINX 1.7.11 or newer
     #
-    # # Pass chunked request bodies to gitlab-git-http-server as-is
+    # # Pass chunked request bodies to gitlab-workhorse as-is
     # proxy_request_buffering off;
     # proxy_http_version 1.1;
 
@@ -138,7 +156,7 @@ server {
     proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
     proxy_set_header    X-Forwarded-Proto   $scheme;
 
-    proxy_pass http://gitlab-git-http-server;
+    proxy_pass http://gitlab-workhorse;
   }
 
   ## Enable gzip compression as per rails guide:
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 7dabfba87e22f73c48d6147f949d7e6f2a2c3a8b..4d31e31f8d57a9f752de0d7d22a46a04f711113f 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -42,8 +42,8 @@ upstream gitlab {
   server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
 }
 
-upstream gitlab-git-http-server {
-  server unix:/home/git/gitlab/tmp/sockets/gitlab-git-http-server.socket fail_timeout=0;
+upstream gitlab-workhorse {
+  server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
 }
 
 ## Redirects all HTTP traffic to the HTTPS host
@@ -160,7 +160,25 @@ server {
     proxy_pass http://gitlab;
   }
 
-  location ~ [-\/\w\.]+\.git\/ {
+  location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
+  location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive {
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
+  location ~ ^/api/v3/projects/.*/repository/archive {
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
+  location @gitlab-workhorse {
     ## If you use HTTPS make sure you disable gzip compression
     ## to be safe against BREACH attack.
     gzip off;
@@ -176,7 +194,7 @@ server {
 
     # The following settings only work with NGINX 1.7.11 or newer
     #
-    # # Pass chunked request bodies to gitlab-git-http-server as-is
+    # # Pass chunked request bodies to gitlab-workhorse as-is
     # proxy_request_buffering off;
     # proxy_http_version 1.1;
 
@@ -185,7 +203,7 @@ server {
     proxy_set_header    X-Forwarded-Ssl     on;
     proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
     proxy_set_header    X-Forwarded-Proto   $scheme;
-    proxy_pass http://gitlab-git-http-server;
+    proxy_pass http://gitlab-workhorse;
   }
 
   ## Enable gzip compression as per rails guide:
diff --git a/lib/tasks/ci/migrate.rake b/lib/tasks/ci/migrate.rake
deleted file mode 100644
index 1de664c85e160a8306a1af46b838f731656d9397..0000000000000000000000000000000000000000
--- a/lib/tasks/ci/migrate.rake
+++ /dev/null
@@ -1,87 +0,0 @@
-namespace :ci do
-  desc 'GitLab | Import and migrate CI database'
-  task migrate: :environment do
-    warn_user_is_not_gitlab
-    configure_cron_mode
-
-    unless ENV['force'] == 'yes'
-      puts 'This will remove all CI related data and restore it from the provided backup.'
-      ask_to_continue
-      puts ''
-    end
-
-    # disable CI for time of migration
-    enable_ci(false)
-
-    # unpack archives
-    migrate = Ci::Migrate::Manager.new
-    migrate.unpack
-
-    Rake::Task['ci:migrate:db'].invoke
-    Rake::Task['ci:migrate:builds'].invoke
-    Rake::Task['ci:migrate:tags'].invoke
-    Rake::Task['ci:migrate:services'].invoke
-
-    # enable CI for time of migration
-    enable_ci(true)
-
-    migrate.cleanup
-  end
-
-  namespace :migrate do
-    desc 'GitLab | Import CI database'
-    task db: :environment do
-      configure_cron_mode
-      $progress.puts 'Restoring database ... '.blue
-      Ci::Migrate::Database.new.restore
-      $progress.puts 'done'.green
-    end
-
-    desc 'GitLab | Import CI builds'
-    task builds: :environment do
-      configure_cron_mode
-      $progress.puts 'Restoring builds ... '.blue
-      Ci::Migrate::Builds.new.restore
-      $progress.puts 'done'.green
-    end
-
-    desc 'GitLab | Migrate CI tags'
-    task tags: :environment do
-      configure_cron_mode
-      $progress.puts 'Migrating tags ... '.blue
-      ::Ci::Migrate::Tags.new.restore
-      $progress.puts 'done'.green
-    end
-
-    desc 'GitLab | Migrate CI auto-increments'
-    task autoincrements: :environment do
-      c = ActiveRecord::Base.connection
-      c.tables.select { |t| t.start_with?('ci_') }.each do |table|
-        result = c.select_one("SELECT id FROM #{table} ORDER BY id DESC LIMIT 1")
-        if result
-          ai_val = result['id'].to_i + 1
-          puts "Resetting auto increment ID for #{table} to #{ai_val}"
-          if c.adapter_name == 'PostgreSQL'
-            c.execute("ALTER SEQUENCE #{table}_id_seq RESTART WITH #{ai_val}")
-          else
-            c.execute("ALTER TABLE #{table} AUTO_INCREMENT = #{ai_val}")
-          end
-        end
-      end
-    end
-
-    desc 'GitLab | Migrate CI services'
-    task services: :environment do
-      $progress.puts 'Migrating services ... '.blue
-      c = ActiveRecord::Base.connection
-      c.execute("UPDATE ci_services SET type=CONCAT('Ci::', type) WHERE type NOT LIKE 'Ci::%'")
-      $progress.puts 'done'.green
-    end
-  end
-
-  def enable_ci(enabled)
-    settings = ApplicationSetting.current || ApplicationSetting.create_from_defaults
-    settings.ci_enabled = enabled
-    settings.save!
-  end
-end
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 66f1ecf385f4f24979905b6e7c8061532d93348d..a25fac62cfc1a392158574aca60d70669dfb9977 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -335,7 +335,7 @@ namespace :gitlab do
       print "Redis version >= #{min_redis_version}? ... "
 
       redis_version = run(%W(redis-cli --version))
-      redis_version = redis_version.try(:match, /redis-cli (.*)/)
+      redis_version = redis_version.try(:match, /redis-cli (\d+\.\d+\.\d+)/)
       if redis_version &&
           (Gem::Version.new(redis_version[1]) > Gem::Version.new(min_redis_version))
         puts "yes".green
@@ -642,7 +642,6 @@ namespace :gitlab do
 
       if Gitlab.config.incoming_email.enabled
         check_address_formatted_correctly
-        check_mail_room_config_exists
         check_imap_authentication
 
         if Rails.env.production?
@@ -744,42 +743,16 @@ namespace :gitlab do
       end
     end
 
-    def check_mail_room_config_exists
-      print "MailRoom config exists? ... "
-
-      mail_room_config_file = Rails.root.join("config", "mail_room.yml")
-
-      if File.exists?(mail_room_config_file)
-        puts "yes".green
-      else
-        puts "no".red
-        try_fixing_it(
-          "Copy config/mail_room.yml.example to config/mail_room.yml",
-          "Check that the information in config/mail_room.yml is correct"
-        )
-        for_more_information(
-          "doc/incoming_email/README.md"
-        )
-        fix_and_rerun
-      end
-    end
-
     def check_imap_authentication
       print "IMAP server credentials are correct? ... "
 
-      mail_room_config_file = Rails.root.join("config", "mail_room.yml")
-
-      unless File.exists?(mail_room_config_file)
-        puts "can't check because of previous errors".magenta
-        return
-      end
-
-      config = YAML.load_file(mail_room_config_file)[:mailboxes].first rescue nil
+      config = Gitlab.config.incoming_email
 
       if config
         begin
-          imap = Net::IMAP.new(config[:host], port: config[:port], ssl: config[:ssl])
-          imap.login(config[:email], config[:password])
+          imap = Net::IMAP.new(config.host, port: config.port, ssl: config.ssl)
+          imap.starttls if config.start_tls
+          imap.login(config.user, config.password)
           connected = true
         rescue
           connected = false
@@ -791,7 +764,7 @@ namespace :gitlab do
       else
         puts "no".red
         try_fixing_it(
-          "Check that the information in config/mail_room.yml is correct"
+          "Check that the information in config/gitlab.yml is correct"
         )
         for_more_information(
           "doc/incoming_email/README.md"
@@ -851,7 +824,7 @@ namespace :gitlab do
         repo_dirs = Dir.glob(File.join(namespace_dir, '*'))
         repo_dirs.each do |dir|
           puts "\nChecking repo at #{dir}"
-          system(*%w(git fsck), chdir: dir)
+          system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: dir)
         end
       end
     end
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 3c0cc763d171dc0c8e0999169af09ae8f99ef0eb..dd61632e5573b3229c0666c0ff904a778553c2cd 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -17,7 +17,7 @@ namespace :gitlab do
 
       # Clone if needed
       unless File.directory?(target_dir)
-        system(*%W(git clone -- #{args.repo} #{target_dir}))
+        system(*%W(#{Gitlab.config.git.bin_path} clone -- #{args.repo} #{target_dir}))
       end
 
       # Make sure we're on the right tag
@@ -27,7 +27,7 @@ namespace :gitlab do
         reseted = reset_to_commit(args)
 
         unless reseted
-          system(*%W(git fetch origin))
+          system(*%W(#{Gitlab.config.git.bin_path} fetch origin))
           reset_to_commit(args)
         end
 
@@ -128,14 +128,14 @@ namespace :gitlab do
   end
 
   def reset_to_commit(args)
-    tag, status = Gitlab::Popen.popen(%W(git describe -- #{args.tag}))
+    tag, status = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} describe -- #{args.tag}))
 
     unless status.zero?
-      tag, status = Gitlab::Popen.popen(%W(git describe -- origin/#{args.tag}))
+      tag, status = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} describe -- origin/#{args.tag}))
     end
 
     tag = tag.strip
-    system(*%W(git reset --hard #{tag}))
+    system(*%W(#{Gitlab.config.git.bin_path} reset --hard #{tag}))
   end
 end
 
diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake
index bf6894a8351930197172d9a3d2a35ad2ee809ae1..141a0b74ec0b4a8151b40fd3320dfccd613f6888 100644
--- a/lib/tasks/migrate/setup_postgresql.rake
+++ b/lib/tasks/migrate/setup_postgresql.rake
@@ -1,6 +1,8 @@
 require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lower_indexes')
+require Rails.root.join('db/migrate/20151008110232_add_users_lower_username_email_indexes')
 
 desc 'GitLab | Sets up PostgreSQL'
 task setup_postgresql: :environment do
   NamespacesProjectsPathLowerIndexes.new.up
+  AddUsersLowerUsernameEmailIndexes.new.up
 end
diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake
index c8881be0954cefb61356af6adff0b830976807b2..d5a96fd38f4776459c55ab2d7779625f14e92a23 100644
--- a/lib/tasks/spinach.rake
+++ b/lib/tasks/spinach.rake
@@ -5,7 +5,7 @@ namespace :spinach do
   task :project do
     cmds = [
       %W(rake gitlab:setup),
-      %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets),
+      %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@commits),
     ]
     run_commands(cmds)
   end
@@ -14,7 +14,7 @@ namespace :spinach do
   task :other do
     cmds = [
       %W(rake gitlab:setup),
-      %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets),
+      %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets,@commits),
     ]
     run_commands(cmds)
   end
@@ -33,4 +33,4 @@ def run_commands(cmds)
   cmds.each do |cmd|
     system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!")
   end
-end
+end
\ No newline at end of file
diff --git a/public/uploads/.gitkeep b/shared/.gitkeep
similarity index 100%
rename from public/uploads/.gitkeep
rename to shared/.gitkeep
diff --git a/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb b/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..34cd9f7e4eb398bd64d8d65226d15bc7488767c4
--- /dev/null
+++ b/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Gitlab::Markdown::ReferenceFilter, benchmark: true do
+  let(:input) do
+    html = <<-EOF
+<p>Hello @alice and @bob, how are you doing today?</p>
+<p>This is simple @dummy text to see how the @ReferenceFilter class performs
+when @processing HTML.</p>
+    EOF
+
+    Nokogiri::HTML.fragment(html)
+  end
+
+  let(:project) { create(:empty_project) }
+
+  let(:filter) { described_class.new(input, project: project) }
+
+  describe '#replace_text_nodes_matching' do
+    let(:iterations) { 6000 }
+
+    describe 'with identical input and output HTML' do
+      benchmark_subject do
+        filter.replace_text_nodes_matching(User.reference_pattern) do |content|
+          content
+        end
+      end
+
+      it { is_expected.to iterate_per_second(iterations) }
+    end
+
+    describe 'with different input and output HTML' do
+      benchmark_subject do
+        filter.replace_text_nodes_matching(User.reference_pattern) do |content|
+          '@eve'
+        end
+      end
+
+      it { is_expected.to iterate_per_second(iterations) }
+    end
+  end
+end
diff --git a/spec/benchmarks/models/milestone_spec.rb b/spec/benchmarks/models/milestone_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a94afc4c40d899a89b0813e5ccfef31799ec7eee
--- /dev/null
+++ b/spec/benchmarks/models/milestone_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Milestone, benchmark: true do
+  describe '#sort_issues' do
+    let(:milestone) { create(:milestone) }
+
+    let(:issue1) { create(:issue, milestone: milestone) }
+    let(:issue2) { create(:issue, milestone: milestone) }
+    let(:issue3) { create(:issue, milestone: milestone) }
+
+    let(:issue_ids) { [issue3.id, issue2.id, issue1.id] }
+
+    benchmark_subject { milestone.sort_issues(issue_ids) }
+
+    it { is_expected.to iterate_per_second(500) }
+  end
+end
diff --git a/spec/benchmarks/models/project_team_spec.rb b/spec/benchmarks/models/project_team_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8b039ef731738b6f6afb8873b29ecded195f5c48
--- /dev/null
+++ b/spec/benchmarks/models/project_team_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe ProjectTeam, benchmark: true do
+  describe '#max_member_access' do
+    let(:group)   { create(:group) }
+    let(:project) { create(:empty_project, group: group) }
+    let(:user)    { create(:user) }
+
+    before do
+      project.team << [user, :master]
+
+      5.times do
+        project.team << [create(:user), :reporter]
+
+        project.group.add_user(create(:user), :reporter)
+      end
+    end
+
+    benchmark_subject { project.team.max_member_access(user.id) }
+
+    it { is_expected.to iterate_per_second(35000) }
+  end
+end
diff --git a/spec/benchmarks/models/user_spec.rb b/spec/benchmarks/models/user_spec.rb
index 168be20b7a576f3119ff492a591d44efe786ed7c..1be7a8d3ed9dfb591f8915245191565f64788ffb 100644
--- a/spec/benchmarks/models/user_spec.rb
+++ b/spec/benchmarks/models/user_spec.rb
@@ -1,6 +1,16 @@
 require 'spec_helper'
 
 describe User, benchmark: true do
+  describe '.all' do
+    before do
+      10.times { create(:user) }
+    end
+
+    benchmark_subject { User.all.to_a }
+
+    it { is_expected.to iterate_per_second(500) }
+  end
+
   describe '.by_login' do
     before do
       %w{Alice Bob Eve}.each do |name|
@@ -11,7 +21,9 @@ describe User, benchmark: true do
       end
     end
 
-    let(:iterations) { 1000 }
+    # The iteration count is based on the query taking little over 1 ms when
+    # using PostgreSQL.
+    let(:iterations) { 900 }
 
     describe 'using a capitalized username' do
       benchmark_subject { User.by_login('Alice') }
@@ -37,4 +49,30 @@ describe User, benchmark: true do
       it { is_expected.to iterate_per_second(iterations) }
     end
   end
+
+  describe '.find_by_any_email' do
+    let(:user) { create(:user) }
+
+    describe 'using a user with only a single Email address' do
+      let(:email) { user.email }
+
+      benchmark_subject { User.find_by_any_email(email) }
+
+      it { is_expected.to iterate_per_second(1000) }
+    end
+
+    describe 'using a user with multiple Email addresses' do
+      let(:email) { user.emails.first.email }
+
+      benchmark_subject { User.find_by_any_email(email) }
+
+      before do
+        10.times do
+          user.emails.create(email: FFaker::Internet.email)
+        end
+      end
+
+      it { is_expected.to iterate_per_second(1000) }
+    end
+  end
 end
diff --git a/spec/benchmarks/services/projects/create_service_spec.rb b/spec/benchmarks/services/projects/create_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..25ed48c34fdb5c8464f0eefbc50e92dd41095540
--- /dev/null
+++ b/spec/benchmarks/services/projects/create_service_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Projects::CreateService, benchmark: true do
+  describe '#execute' do
+    let(:user) { create(:user, :admin) }
+
+    let(:group) do
+      group = create(:group)
+
+      create(:group_member, group: group, user: user)
+
+      group
+    end
+
+    benchmark_subject do
+      name    = SecureRandom.hex
+      service = described_class.new(user,
+                                    name:             name,
+                                    path:             name,
+                                    namespace_id:     group.id,
+                                    visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+
+      service.execute
+    end
+
+    it { is_expected.to iterate_per_second(0.5) }
+  end
+end
diff --git a/spec/controllers/abuse_reports_controller_spec.rb b/spec/controllers/abuse_reports_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0faab8d7ff0b2e3885325bf5a9f48947bcc55111
--- /dev/null
+++ b/spec/controllers/abuse_reports_controller_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+describe AbuseReportsController do
+  let(:reporter)    { create(:user) }
+  let(:user)        { create(:user) }
+  let(:message)     { "This user is a spammer" }
+
+  before do
+    sign_in(reporter)
+  end
+
+  describe "POST create" do
+    context "with admin notification email set" do
+      let(:admin_email) { "admin@example.com"}
+
+      before(:each) do
+        stub_application_setting(admin_notification_email: admin_email)
+      end
+
+      it "sends a notification email" do
+        post :create,
+          abuse_report: {
+            user_id: user.id,
+            message: message
+          }
+
+        email = ActionMailer::Base.deliveries.last
+
+        expect(email.to).to eq([admin_email])
+        expect(email.subject).to include(user.username)
+        expect(email.text_part.body).to include(message)
+      end
+
+      it "saves the abuse report" do
+        expect do
+          post :create,
+            abuse_report: {
+              user_id: user.id,
+              message: message
+            }
+        end.to change { AbuseReport.count }.by(1)
+      end
+    end
+
+    context "without admin notification email set" do
+      before(:each) do
+        stub_application_setting(admin_notification_email: nil)
+      end
+
+      it "does not send a notification email" do
+        expect do
+          post :create,
+            abuse_report: {
+              user_id: user.id,
+              message: message
+            }
+        end.not_to change { ActionMailer::Base.deliveries.count }
+      end
+
+      it "saves the abuse report" do
+        expect do
+          post :create,
+            abuse_report: {
+              user_id: user.id,
+              message: message
+            }
+        end.to change { AbuseReport.count }.by(1)
+      end
+    end
+  end
+
+end
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 7168db117d605e2cbdf4fb880d4c64cf23eb1d96..fcbe62cace85c9f243e489679fb82b486d8fc488 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -37,6 +37,32 @@ describe Admin::UsersController do
     end
   end
 
+  describe 'PUT block/:id' do
+    let(:user) { create(:user) }
+
+    it 'blocks user' do
+      put :block, id: user.username
+      user.reload
+      expect(user.blocked?).to be_truthy
+      expect(flash[:notice]).to eq 'Successfully blocked'
+    end
+  end
+
+  describe 'PUT unblock/:id' do
+    let(:user) { create(:user) }
+
+    before do
+      user.block
+    end
+
+    it 'unblocks user' do
+      put :unblock, id: user.username
+      user.reload
+      expect(user.blocked?).to be_falsey
+      expect(flash[:notice]).to eq 'Successfully unblocked'
+    end
+  end
+
   describe 'PUT unlock/:id' do
     let(:user) { create(:user) }
 
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index 766be578f7f34be0ba9b01e47f5f232efe378105..bbf8adef534548e2c4dcff99a154e15058ecda87 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -41,7 +41,7 @@ describe Import::GithubController do
 
     it "assigns variables" do
       @project = create(:project, import_type: 'github', creator_id: user.id)
-      stub_client(repos: [@repo], orgs: [@org], org_repos: [@org_repo])
+      stub_client(repos: [@repo, @org_repo], orgs: [@org], org_repos: [@org_repo])
 
       get :status
 
diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3c6e54839b5eb1d0ef5d76211aa9dfa81493c04d
--- /dev/null
+++ b/spec/controllers/invites_controller_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe InvitesController do
+  let(:token) { '123456' }
+  let(:user) { create(:user) }
+  let(:member) { create(:project_member, invite_token: token, invite_email: 'test@abc.com', user: user) }
+
+  before do
+    controller.instance_variable_set(:@member, member)
+    sign_in(user)
+  end
+
+  describe 'GET #accept' do
+    it 'accepts user' do
+      get :accept, id: token
+      member.reload
+
+      expect(response.status).to eq(302)
+      expect(member.user).to eq(user)
+      expect(flash[:notice]).to include 'You have been granted'
+    end
+  end
+
+  describe 'GET #decline' do
+    it 'declines user' do
+      get :decline, id: token
+      expect{member.reload}.to raise_error ActiveRecord::RecordNotFound
+
+      expect(response.status).to eq(302)
+      expect(flash[:notice]).to include 'You have declined the invitation to join'
+    end
+  end
+end
diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb
index 91856ed0cc0341fbd49a0e7d98be6ae3bd63c385..18a30033ed8ed73dd048fc155bd00cb22957d174 100644
--- a/spec/controllers/projects/repositories_controller_spec.rb
+++ b/spec/controllers/projects/repositories_controller_spec.rb
@@ -33,33 +33,5 @@ describe Projects::RepositoriesController do
         expect(response.status).to eq(404)
       end
     end
-
-    context "when the service doesn't return a path" do
-
-      before do
-        allow(service).to receive(:execute).and_return(nil)
-      end
-
-      it "reloads the page" do
-        get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
-
-        expect(response).to redirect_to(archive_namespace_project_repository_path(project.namespace, project, ref: "master", format: "zip"))
-      end
-    end
-
-    context "when the service returns a path" do
-
-      let(:path) { Rails.root.join("spec/fixtures/dk.png").to_s }
-
-      before do
-        allow(service).to receive(:execute).and_return(path)
-      end
-
-      it "sends the file" do
-        get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
-
-        expect(response.body).to eq(File.binread(path))
-      end
-    end
   end
 end
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb
index d4ecd98e12d2038e1e499c21ffeb9df386942950..ccd8c741c832427ade04df1b95499031c2ba8700 100644
--- a/spec/controllers/projects/services_controller_spec.rb
+++ b/spec/controllers/projects/services_controller_spec.rb
@@ -10,26 +10,43 @@ describe Projects::ServicesController do
     project.team << [user, :master]
     controller.instance_variable_set(:@project, project)
     controller.instance_variable_set(:@service, service)
-    request.env["HTTP_REFERER"] = "/"
   end
 
-  describe "#test" do
-    context 'success' do
-      it "should redirect and show success message" do
-        expect(service).to receive(:test).and_return({ success: true, result: 'done' })
-        get :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, format: :html
-        expect(response.status).to redirect_to('/')
-        expect(flash[:notice]).to eq('We sent a request to the provided URL')
-      end
+  shared_examples_for 'services controller' do |referrer|
+    before do
+      request.env["HTTP_REFERER"] = referrer
     end
 
-    context 'failure' do
-      it "should redirect and show failure message" do
-        expect(service).to receive(:test).and_return({ success: false, result: 'Bad test' })
-        get :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, format: :html
-        expect(response.status).to redirect_to('/')
-        expect(flash[:alert]).to eq('We tried to send a request to the provided URL but an error occurred: Bad test')
+    describe "#test" do
+      context 'success' do
+        it "should redirect and show success message" do
+          expect(service).to receive(:test).and_return({ success: true, result: 'done' })
+          get :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, format: :html
+          expect(response.status).to redirect_to('/')
+          expect(flash[:notice]).to eq('We sent a request to the provided URL')
+        end
+      end
+
+      context 'failure' do
+        it "should redirect and show failure message" do
+          expect(service).to receive(:test).and_return({ success: false, result: 'Bad test' })
+          get :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, format: :html
+          expect(response.status).to redirect_to('/')
+          expect(flash[:alert]).to eq('We tried to send a request to the provided URL but an error occurred: Bad test')
+        end
       end
     end
   end
+
+  describe 'referrer defined' do
+    it_should_behave_like 'services controller' do
+      let!(:referrer) { "/" }
+    end
+  end
+
+  describe 'referrer undefined' do
+    it_should_behave_like 'services controller' do
+      let!(:referrer) { nil }
+    end
+  end
 end
diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb
index f51abfedae529db22d8ec3705233e27e1d0dc61f..93c4494c6607ef084889c7b8faab31158d4bd433 100644
--- a/spec/controllers/projects/uploads_controller_spec.rb
+++ b/spec/controllers/projects/uploads_controller_spec.rb
@@ -33,7 +33,7 @@ describe Projects::UploadsController do
 
       it 'returns a content with original filename, new link, and correct type.' do
         expect(response.body).to match '\"alt\":\"rails_sample\"'
-        expect(response.body).to match "\"url\":\"http://localhost/#{project.path_with_namespace}/uploads"
+        expect(response.body).to match "\"url\":\"/uploads"
         expect(response.body).to match '\"is_image\":true'
       end
     end
@@ -49,7 +49,7 @@ describe Projects::UploadsController do
 
       it 'returns a content with original filename, new link, and correct type.' do
         expect(response.body).to match '\"alt\":\"doc_sample.txt\"'
-        expect(response.body).to match "\"url\":\"http://localhost/#{project.path_with_namespace}/uploads"
+        expect(response.body).to match "\"url\":\"/uploads"
         expect(response.body).to match '\"is_image\":false'
       end
     end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 21beaf37fcea7de541182193b1d5853a180c6aec..4bb47c6b02559c73c815f659801c16060aa13b80 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -22,17 +22,68 @@ describe ProjectsController do
       end
     end
 
-    context "when requested with case sensitive namespace and project path" do
-      it "redirects to the normalized path for case mismatch" do
-        get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase
+    context "rendering default project view" do
+      render_views
+
+      it "renders the activity view" do
+        allow(controller).to receive(:current_user).and_return(user)
+        allow(user).to receive(:project_view).and_return('activity')
+
+        get :show, namespace_id: public_project.namespace.path, id: public_project.path
+        expect(response).to render_template('_activity')
+      end
+
+      it "renders the readme view" do
+        allow(controller).to receive(:current_user).and_return(user)
+        allow(user).to receive(:project_view).and_return('readme')
 
-        expect(response).to redirect_to("/#{public_project.path_with_namespace}")
+        get :show, namespace_id: public_project.namespace.path, id: public_project.path
+        expect(response).to render_template('_readme')
       end
 
-      it "loads the page if normalized path matches request path" do
+      it "renders the files view" do
+        allow(controller).to receive(:current_user).and_return(user)
+        allow(user).to receive(:project_view).and_return('files')
+
         get :show, namespace_id: public_project.namespace.path, id: public_project.path
+        expect(response).to render_template('_files')
+      end
+    end
+
+    context "when requested with case sensitive namespace and project path" do
+      context "when there is a match with the same casing" do
+        it "loads the project" do
+          get :show, namespace_id: public_project.namespace.path, id: public_project.path
+
+          expect(assigns(:project)).to eq(public_project)
+          expect(response.status).to eq(200)
+        end
+      end
+
+      context "when there is a match with different casing" do
+        it "redirects to the normalized path" do
+          get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase
+
+          expect(assigns(:project)).to eq(public_project)
+          expect(response).to redirect_to("/#{public_project.path_with_namespace}")
+        end
+
+
+        # MySQL queries are case insensitive by default, so this spec would fail.
+        if Gitlab::Database.postgresql?
+          context "when there is also a match with the same casing" do
 
-        expect(response.status).to eq(200)
+            let!(:other_project) { create(:project, :public, namespace: public_project.namespace, path: public_project.path.upcase) }
+
+            it "loads the exactly matched project" do
+
+              get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase
+
+              expect(assigns(:project)).to eq(other_project)
+              expect(response.status).to eq(200)
+            end
+          end
+        end
       end
     end
   end
@@ -62,4 +113,50 @@ describe ProjectsController do
       expect(user.starred?(public_project)).to be_falsey
     end
   end
+
+  describe "DELETE remove_fork" do
+    context 'when signed in' do
+      before do
+        sign_in(user)
+      end
+
+      context 'with forked project' do
+        let(:project_fork) { create(:project, namespace: user.namespace) }
+
+        before do
+          create(:forked_project_link, forked_to_project: project_fork)
+        end
+
+        it 'should remove fork from project' do
+          delete(:remove_fork,
+              namespace_id: project_fork.namespace.to_param,
+              id: project_fork.to_param, format: :js)
+
+          expect(project_fork.forked?).to be_falsey
+          expect(flash[:notice]).to eq('The fork relationship has been removed.')
+          expect(response).to render_template(:remove_fork)
+        end
+      end
+
+      context 'when project not forked' do
+        let(:unforked_project) { create(:project, namespace: user.namespace) }
+
+        it 'should do nothing if project was not forked' do
+          delete(:remove_fork,
+              namespace_id: unforked_project.namespace.to_param,
+              id: unforked_project.to_param, format: :js)
+
+          expect(flash[:notice]).to be_nil
+          expect(response).to render_template(:remove_fork)
+        end
+      end
+    end
+
+    it "does nothing if user is not signed in" do
+      delete(:remove_fork,
+          namespace_id: project.namespace.to_param,
+          id: project.to_param, format: :js)
+      expect(response.status).to eq(401)
+    end
+  end
 end
diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb
index 111e1a8281680219c26870507ab8e3d35e687293..1183a190353e7f97e827c0709278712e5e625e89 100644
--- a/spec/factories/ci/projects.rb
+++ b/spec/factories/ci/projects.rb
@@ -33,6 +33,8 @@ FactoryGirl.define do
 
     gl_project factory: :empty_project
 
+    shared_runners_enabled false
+
     factory :ci_project do
       token 'iPWx6WM4lhHNedGfBpPJNP'
     end
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
new file mode 100644
index 0000000000000000000000000000000000000000..80d6bbee6c75ad952decf928fc574ff913d8815f
--- /dev/null
+++ b/spec/factories/releases.rb
@@ -0,0 +1,9 @@
+# Read about factories at https://github.com/thoughtbot/factory_girl
+
+FactoryGirl.define do
+  factory :release do
+    tag "v1.1.0"
+    description "Awesome release"
+    project
+  end
+end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 924047a0d8fafd2d49d0a73f7bf6c1af973bfbd6..158e85e598f51d848bfff1910a28792b9a1e6949 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -9,6 +9,55 @@ describe "Builds" do
     @gl_project.team << [@user, :master]
   end
 
+  describe "GET /:project/builds" do
+    context "Running scope" do
+      before do
+        @build.run!
+        visit namespace_project_builds_path(@gl_project.namespace, @gl_project)
+      end
+
+      it { expect(page).to have_content 'Running' }
+      it { expect(page).to have_content 'Cancel all' }
+      it { expect(page).to have_content @build.short_sha }
+      it { expect(page).to have_content @build.ref }
+      it { expect(page).to have_content @build.name }
+    end
+
+    context "Finished scope" do
+      before do
+        @build.run!
+        visit namespace_project_builds_path(@gl_project.namespace, @gl_project, scope: :finished)
+      end
+
+      it { expect(page).to have_content 'No builds to show' }
+      it { expect(page).to have_content 'Cancel all' }
+    end
+
+    context "All builds" do
+      before do
+        @gl_project.ci_builds.running_or_pending.each(&:success)
+        visit namespace_project_builds_path(@gl_project.namespace, @gl_project, scope: :all)
+      end
+
+      it { expect(page).to have_content 'All' }
+      it { expect(page).to have_content @build.short_sha }
+      it { expect(page).to have_content @build.ref }
+      it { expect(page).to have_content @build.name }
+      it { expect(page).to_not have_content 'Cancel all' }
+    end
+  end
+
+  describe "POST /:project/builds/:id/cancel_all" do
+    before do
+      @build.run!
+      visit namespace_project_builds_path(@gl_project.namespace, @gl_project)
+      click_link "Cancel all"
+    end
+
+    it { expect(page).to have_content 'No builds to show' }
+    it { expect(page).to_not have_content 'Cancel all' }
+  end
+
   describe "GET /:project/builds/:id" do
     before do
       visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
@@ -19,10 +68,11 @@ describe "Builds" do
     it { expect(page).to have_content @commit.git_author_name }
   end
 
-  describe "GET /:project/builds/:id/cancel" do
+  describe "POST /:project/builds/:id/cancel" do
     before do
       @build.run!
-      visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+      visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+      click_link "Cancel"
     end
 
     it { expect(page).to have_content 'canceled' }
@@ -31,7 +81,9 @@ describe "Builds" do
 
   describe "POST /:project/builds/:id/retry" do
     before do
-      visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+      @build.run!
+      visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+      click_link "Cancel"
       click_link 'Retry'
     end
 
diff --git a/spec/features/ci/events_spec.rb b/spec/features/ci/events_spec.rb
deleted file mode 100644
index 5b9fd40415996461750d786acad256952995240e..0000000000000000000000000000000000000000
--- a/spec/features/ci/events_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'spec_helper'
-
-describe "Events" do
-  let(:user)    { create(:user) }
-  let(:project) { FactoryGirl.create :ci_project }
-  let(:event)   { FactoryGirl.create :ci_admin_event, project: project }
-
-  before do
-    login_as(user)
-    project.gl_project.team << [user, :master]
-  end
-
-  describe "GET /ci/project/:id/events" do
-    before do
-      event
-      visit ci_project_events_path(project)
-    end
-
-    it { expect(page).to have_content "Events" }
-    it { expect(page).to have_content event.description }
-  end
-end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 1adc2cdf70a235bfaab9d1b8b3433c033ef077d3..340924fafe7566ea34d5c8bd4e5baf1bcfedb6ff 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -32,7 +32,7 @@ describe "Commits" do
     describe "Cancel all builds" do
       it "cancels commit" do
         visit ci_status_path(@commit)
-        click_on "Cancel all"
+        click_on "Cancel running"
         expect(page).to have_content "canceled"
       end
     end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index c557a1061af269741f7c9d674e57b7d5ba994fdb..fdd8cf07b12cf14c792caaaac76296ac368206f1 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -220,7 +220,7 @@ describe 'GitLab Markdown', feature: true do
     end
   end
 
-  # `markdown` calls these two methods
+  # Fake a `current_user` helper
   def current_user
     @feat.user
   end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index aac93b17a3800dee846bd33ca872f85cb3621f0a..09fcff2444ac83771442c68f0935fd591fdd4305 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -34,6 +34,27 @@ feature 'Project', feature: true do
     end
   end
 
+  describe 'remove forked relationship', js: true do
+    let(:user)    { create(:user) }
+    let(:project) { create(:project, namespace: user.namespace) }
+
+    before do
+      login_with user
+      create(:forked_project_link, forked_to_project: project)
+      visit edit_namespace_project_path(project.namespace, project)
+    end
+
+    it 'should remove fork' do
+      expect(page).to have_content 'Remove fork relationship'
+
+      remove_with_confirm('Remove fork relationship', project.path)
+
+      expect(page).to have_content 'The fork relationship has been removed.'
+      expect(project.forked?).to be_falsey
+      expect(page).not_to have_content 'Remove fork relationship'
+    end
+  end
+
   describe 'removal', js: true do
     let(:user)    { create(:user) }
     let(:project) { create(:project, namespace: user.namespace) }
@@ -45,13 +66,13 @@ feature 'Project', feature: true do
     end
 
     it 'should remove project' do
-      expect { remove_project }.to change {Project.count}.by(-1)
+      expect { remove_with_confirm('Remove project', project.path) }.to change {Project.count}.by(-1)
     end
   end
 
-  def remove_project
-    click_button "Remove project"
-    fill_in 'confirm_name_input', with: project.path
+  def remove_with_confirm(button_text, confirm_with)
+    click_button button_text
+    fill_in 'confirm_name_input', with: confirm_with
     click_button 'Confirm'
   end
 end
diff --git a/spec/finders/group_finder_spec.rb b/spec/finders/group_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..78dc027837cf571b23a04e8344c3c168240991a5
--- /dev/null
+++ b/spec/finders/group_finder_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe GroupsFinder do
+  let(:user) { create :user }
+  let!(:group) { create :group }
+  let!(:public_group) { create :group, public: true }
+  
+  describe :execute do
+    it 'finds public group' do
+      groups = GroupsFinder.new.execute(user)
+      expect(groups.size).to eq(1)
+      expect(groups.first).to eq(public_group)
+    end
+  end
+end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 742420f550e0591fa1bf5ea8ee651671a717b6bf..1dfae0fbd3fea83a4f0966f20f27d5cfbeb880f2 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -99,6 +99,15 @@ describe ApplicationHelper do
 
       helper.avatar_icon('foo@example.com', 20)
     end
+
+    describe 'using a User' do
+      it 'should return an URL for the avatar' do
+        user = create(:user, avatar: File.open(avatar_file_path))
+
+        expect(helper.avatar_icon(user).to_s).
+          to match("/uploads/user/avatar/#{user.id}/banana_sample.gif")
+      end
+    end
   end
 
   describe 'gravatar_icon' do
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 20ae29e2bd3c68b35178707fee57999984154559..762ec25c4f5555305e1c741f6f852d272ecf179d 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -11,12 +11,15 @@ describe GitlabMarkdownHelper do
   let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
   let(:snippet)       { create(:project_snippet, project: project) }
 
-  # Helper expects a current_user method.
-  let(:current_user) { user }
-
   before do
+    # Ensure the generated reference links aren't redacted
+    project.team << [user, :master]
+
     # Helper expects a @project instance variable
-    @project = project
+    helper.instance_variable_set(:@project, project)
+
+    # Stub the `current_user` helper
+    allow(helper).to receive(:current_user).and_return(user)
   end
 
   describe "#markdown" do
@@ -25,23 +28,23 @@ describe GitlabMarkdownHelper do
 
       it "should link to the merge request" do
         expected = namespace_project_merge_request_path(project.namespace, project, merge_request)
-        expect(markdown(actual)).to match(expected)
+        expect(helper.markdown(actual)).to match(expected)
       end
 
       it "should link to the commit" do
         expected = namespace_project_commit_path(project.namespace, project, commit)
-        expect(markdown(actual)).to match(expected)
+        expect(helper.markdown(actual)).to match(expected)
       end
 
       it "should link to the issue" do
         expected = namespace_project_issue_path(project.namespace, project, issue)
-        expect(markdown(actual)).to match(expected)
+        expect(helper.markdown(actual)).to match(expected)
       end
     end
 
     describe "override default project" do
       let(:actual) { issue.to_reference }
-      let(:second_project) { create(:project) }
+      let(:second_project) { create(:project, :public) }
       let(:second_issue) { create(:issue, project: second_project) }
 
       it 'should link to the issue' do
@@ -56,7 +59,7 @@ describe GitlabMarkdownHelper do
     let(:issues)      { create_list(:issue, 2, project: project) }
 
     it 'should handle references nested in links with all the text' do
-      actual = link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", commit_path)
+      actual = helper.link_to_gfm("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real", commit_path)
       doc = Nokogiri::HTML.parse(actual)
 
       # Make sure we didn't create invalid markup
@@ -86,7 +89,7 @@ describe GitlabMarkdownHelper do
     end
 
     it 'should forward HTML options' do
-      actual = link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo')
+      actual = helper.link_to_gfm("Fixed in #{commit.id}", commit_path, class: 'foo')
       doc = Nokogiri::HTML.parse(actual)
 
       expect(doc.css('a')).to satisfy do |v|
@@ -97,13 +100,13 @@ describe GitlabMarkdownHelper do
 
     it "escapes HTML passed in as the body" do
       actual = "This is a <h1>test</h1> - see #{issues[0].to_reference}"
-      expect(link_to_gfm(actual, commit_path)).
+      expect(helper.link_to_gfm(actual, commit_path)).
         to match('&lt;h1&gt;test&lt;/h1&gt;')
     end
 
     it 'ignores reference links when they are the entire body' do
       text = issues[0].to_reference
-      act = link_to_gfm(text, '/foo')
+      act = helper.link_to_gfm(text, '/foo')
       expect(act).to eq %Q(<a href="/foo">#{issues[0].to_reference}</a>)
     end
 
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index c08ddb4cae1bd6ba25dd6a110b8cb9cead4a67c6..78a6b631eb25cb059abd0a8dfe21fc9e8a885560 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -117,4 +117,14 @@ describe IssuesHelper do
     end
   end
 
+  describe "#merge_requests_sentence" do
+    subject { merge_requests_sentence(merge_requests)}
+    let(:merge_requests) do
+      [ build(:merge_request, iid: 1), build(:merge_request, iid: 2),
+        build(:merge_request, iid: 3)]
+    end
+
+    it { is_expected.to eq("!1, !2, or !3") }
+  end
+
 end
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index fb70a36dc02f84078badbf26d0ae618d8a3178fc..0c8d06b7059bed24926fad705e09c5d8a3299049 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -14,11 +14,6 @@ describe LabelsHelper do
         expect(label).not_to receive(:project)
         link_to_label(label)
       end
-
-      it 'includes option for "No Label"' do
-        result = project_labels_options(project)
-        expect(result).to include('No Label')
-      end
     end
 
     context 'without @project set' do
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 53e56ebff44bd34cb8fc9e23bca8930f449fd49d..f2efb528aeb6cc1c9397e7a5c9a8676286f4085d 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -70,4 +70,18 @@ describe ProjectsHelper do
       expect(helper.send(:readme_cache_key)).to eq("#{project.path_with_namespace}-nil-readme")
     end
   end
+
+  describe 'link_to_member' do
+    let(:group)   { create(:group) }
+    let(:project) { create(:empty_project, group: group) }
+    let(:user)    { create(:user) }
+
+    describe 'using the default options' do
+      it 'returns an HTML link to the user' do
+        link = helper.link_to_member(project, user)
+
+        expect(link).to match(%r{/u/#{user.username}})
+      end
+    end
+  end
 end
diff --git a/spec/helpers/runners_helper_spec.rb b/spec/helpers/runners_helper_spec.rb
index b3d635a193274b56013d00e377e57516474cf404..35f91b7decfde61701c8567d40bcf2fba7499a98 100644
--- a/spec/helpers/runners_helper_spec.rb
+++ b/spec/helpers/runners_helper_spec.rb
@@ -12,7 +12,7 @@ describe RunnersHelper do
   end
 
   it "returns online text" do
-    runner = FactoryGirl.build(:ci_runner, contacted_at: 1.hour.ago, active: true)
+    runner = FactoryGirl.build(:ci_runner, contacted_at: 1.second.ago, active: true)
     expect(runner_status_icon(runner)).to include("Runner is online")
   end
 end
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index b327f4f911a96d29dc78d6df6dae9ae4ee3c8313..ebe9c29d91c72cb426a383ef4fa4291655cdebd4 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -42,6 +42,11 @@ describe SearchHelper do
         expect(search_autocomplete_opts(project.name).size).to eq(1)
       end
 
+      it "includes the public group" do
+        group = create(:group, public: true)
+        expect(search_autocomplete_opts(group.name).size).to eq(1)
+      end
+
       context "with a current project" do
         before { @project = create(:project) }
 
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index aba957da4881694b6795736bfc163b1b682b6786..9963f76f99303271d7e0dace685f3886dc5cf2f3 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -2,7 +2,8 @@ require 'spec_helper'
 
 module Ci
   describe GitlabCiYamlProcessor do
-
+    let(:path) { 'path' }
+    
     describe "#builds_for_ref" do
       let(:type) { 'test' }
 
@@ -12,7 +13,7 @@ module Ci
           rspec: { script: "rspec" }
         })
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+        config_processor = GitlabCiYamlProcessor.new(config, path)
 
         expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
         expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({
@@ -24,81 +25,222 @@ module Ci
           commands: "pwd\nrspec",
           tag_list: [],
           options: {},
-          allow_failure: false
+          allow_failure: false,
+          when: "on_success"
         })
       end
+      
+      describe :only do
+        it "does not return builds if only has another branch" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", only: ["deploy"] }
+                             })
 
-      it "does not return builds if only has another branch" do
-        config = YAML.dump({
-          before_script: ["pwd"],
-          rspec: { script: "rspec", only: ["deploy"] }
-        })
+          config_processor = GitlabCiYamlProcessor.new(config, path)
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
+        end
 
-        expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
-      end
+        it "does not return builds if only has regexp with another branch" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", only: ["/^deploy$/"] }
+                             })
 
-      it "does not return builds if only has regexp with another branch" do
-        config = YAML.dump({
-          before_script: ["pwd"],
-          rspec: { script: "rspec", only: ["/^deploy$/"] }
-        })
+          config_processor = GitlabCiYamlProcessor.new(config, path)
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
+        end
 
-        expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
-      end
+        it "returns builds if only has specified this branch" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", only: ["master"] }
+                             })
 
-      it "returns builds if only has specified this branch" do
-        config = YAML.dump({
-          before_script: ["pwd"],
-          rspec: { script: "rspec", only: ["master"] }
-        })
+          config_processor = GitlabCiYamlProcessor.new(config, path)
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
+        end
 
-        expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
-      end
+        it "returns builds if only has a list of branches including specified" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, only: ["master", "deploy"] }
+                             })
 
-      it "does not build tags" do
-        config = YAML.dump({
-          before_script: ["pwd"],
-          rspec: { script: "rspec", except: ["tags"] }
-        })
+          config_processor = GitlabCiYamlProcessor.new(config, path)
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+        end
 
-        expect(config_processor.builds_for_stage_and_ref(type, "0-1", true).size).to eq(0)
-      end
+        it "returns builds if only has a branches keyword specified" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, only: ["branches"] }
+                             })
 
-      it "returns builds if only has a list of branches including specified" do
-        config = YAML.dump({
-                             before_script: ["pwd"],
-                             rspec: { script: "rspec", type: type, only: ["master", "deploy"] }
-                           })
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+        end
+
+        it "does not return builds if only has a tags keyword" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, only: ["tags"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
+        end
+
+        it "returns builds if only has current repository path" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, only: ["branches@path"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+        end
 
-        expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+        it "does not return builds if only has different repository path" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, only: ["branches@fork"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
+        end
+
+        it "returns build only for specified type" do
+
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: "test", only: ["master", "deploy"] },
+                               staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] },
+                               production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] },
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, 'fork')
+
+          expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2)
+          expect(config_processor.builds_for_stage_and_ref("test", "deploy").size).to eq(1)
+          expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(1)
+        end
       end
 
-      it "returns build only for specified type" do
+      describe :except do
+        it "returns builds if except has another branch" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", except: ["deploy"] }
+                             })
 
-        config = YAML.dump({
-                             before_script: ["pwd"],
-                             build: { script: "build", type: "build", only: ["master", "deploy"] },
-                             rspec: { script: "rspec", type: type, only: ["master", "deploy"] },
-                             staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] },
-                             production: { script: "deploy", type: "deploy", only: ["master", "deploy"] },
-                           })
+          config_processor = GitlabCiYamlProcessor.new(config, path)
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
+        end
 
-        expect(config_processor.builds_for_stage_and_ref("production", "deploy").size).to eq(0)
-        expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
-        expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2)
+        it "returns builds if except has regexp with another branch" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", except: ["/^deploy$/"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
+        end
+
+        it "does not return builds if except has specified this branch" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", except: ["master"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
+        end
+
+        it "does not return builds if except has a list of branches including specified" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, except: ["master", "deploy"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
+        end
+
+        it "does not return builds if except has a branches keyword specified" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, except: ["branches"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
+        end
+
+        it "returns builds if except has a tags keyword" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, except: ["tags"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+        end
+
+        it "does not return builds if except has current repository path" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, except: ["branches@path"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
+        end
+
+        it "returns builds if except has different repository path" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, except: ["branches@fork"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+        end
+
+        it "returns build except specified type" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: "test", except: ["master", "deploy", "test@fork"] },
+                               staging: { script: "deploy", type: "deploy", except: ["master"] },
+                               production: { script: "deploy", type: "deploy", except: ["master@fork"] },
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, 'fork')
+
+          expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2)
+          expect(config_processor.builds_for_stage_and_ref("test", "test").size).to eq(0)
+          expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(0)
+        end
       end
+
     end
 
     describe "Image and service handling" do
@@ -110,7 +252,7 @@ module Ci
                              rspec: { script: "rspec" }
                            })
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+        config_processor = GitlabCiYamlProcessor.new(config, path)
 
         expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
         expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
@@ -125,7 +267,8 @@ module Ci
             image: "ruby:2.1",
             services: ["mysql"]
           },
-          allow_failure: false
+          allow_failure: false,
+          when: "on_success"
         })
       end
 
@@ -137,7 +280,7 @@ module Ci
                              rspec:         { image: "ruby:2.5", services: ["postgresql"], script: "rspec" }
                            })
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+        config_processor = GitlabCiYamlProcessor.new(config, path)
 
         expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
         expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
@@ -152,7 +295,8 @@ module Ci
             image: "ruby:2.5",
             services: ["postgresql"]
           },
-          allow_failure: false
+          allow_failure: false,
+          when: "on_success"
         })
       end
     end
@@ -169,11 +313,26 @@ module Ci
                              rspec: { script: "rspec" }
                            })
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+        config_processor = GitlabCiYamlProcessor.new(config, path)
         expect(config_processor.variables).to eq(variables)
       end
     end
 
+    describe "When" do
+      %w(on_success on_failure always).each do |when_state|
+        it "returns #{when_state} when defined" do
+          config = YAML.dump({
+                               rspec: { script: "rspec", when: when_state }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+          builds = config_processor.builds_for_stage_and_ref("test", "master")
+          expect(builds.size).to eq(1)
+          expect(builds.first[:when]).to eq(when_state)
+        end
+      end
+    end
+
     describe "Error handling" do
       it "indicates that object is invalid" do
         expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError)
@@ -182,135 +341,156 @@ module Ci
       it "returns errors if tags parameter is invalid" do
         config = YAML.dump({ rspec: { script: "test", tags: "mysql" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings")
       end
 
       it "returns errors if before_script parameter is invalid" do
         config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings")
       end
 
       it "returns errors if image parameter is invalid" do
         config = YAML.dump({ image: ["test"], rspec: { script: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string")
       end
 
+      it "returns errors if job name is blank" do
+        config = YAML.dump({ '' => { script: "test" } })
+        expect do
+          GitlabCiYamlProcessor.new(config, path)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string")
+      end
+
+      it "returns errors if job name is non-string" do
+        config = YAML.dump({ 10 => { script: "test" } })
+        expect do
+          GitlabCiYamlProcessor.new(config, path)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string")
+      end
+
       it "returns errors if job image parameter is invalid" do
         config = YAML.dump({ rspec: { script: "test", image: ["test"] } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string")
       end
 
       it "returns errors if services parameter is not an array" do
         config = YAML.dump({ services: "test", rspec: { script: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings")
       end
 
       it "returns errors if services parameter is not an array of strings" do
         config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings")
       end
 
       it "returns errors if job services parameter is not an array" do
         config = YAML.dump({ rspec: { script: "test", services: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings")
       end
 
       it "returns errors if job services parameter is not an array of strings" do
         config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings")
       end
 
       it "returns errors if there are unknown parameters" do
         config = YAML.dump({ extra: "bundle update" })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra")
       end
 
       it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do
         config = YAML.dump({ extra: { services: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra")
       end
 
       it "returns errors if there is no any jobs defined" do
         config = YAML.dump({ before_script: ["bundle update"] })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job")
       end
 
       it "returns errors if job allow_failure parameter is not an boolean" do
         config = YAML.dump({ rspec: { script: "test", allow_failure: "string" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean")
       end
 
       it "returns errors if job stage is not a string" do
         config = YAML.dump({ rspec: { script: "test", type: 1, allow_failure: "string" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy")
       end
 
       it "returns errors if job stage is not a pre-defined stage" do
         config = YAML.dump({ rspec: { script: "test", type: "acceptance", allow_failure: "string" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy")
       end
 
       it "returns errors if job stage is not a defined stage" do
         config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance", allow_failure: "string" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test")
       end
 
       it "returns errors if stages is not an array" do
         config = YAML.dump({ types: "test", rspec: { script: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings")
       end
 
       it "returns errors if stages is not an array of strings" do
         config = YAML.dump({ types: [true, "test"], rspec: { script: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings")
       end
 
       it "returns errors if variables is not a map" do
         config = YAML.dump({ variables: "test", rspec: { script: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings")
       end
 
       it "returns errors if variables is not a map of key-valued strings" do
         config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } })
         expect do
-          GitlabCiYamlProcessor.new(config)
+          GitlabCiYamlProcessor.new(config, path)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings")
       end
+
+      it "returns errors if job when is not on_success, on_failure or always" do
+        config = YAML.dump({ rspec: { script: "test", when: 1 } })
+        expect do
+          GitlabCiYamlProcessor.new(config, path)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always")
+      end
     end
   end
 end
diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb
index 37c527221a0b7c80854f2f48517693bb38523357..dfa0e10318a3ca1d01146ec3221992485a510892 100644
--- a/spec/lib/gitlab/backend/grack_auth_spec.rb
+++ b/spec/lib/gitlab/backend/grack_auth_spec.rb
@@ -50,6 +50,22 @@ describe Grack::Auth do
       end
     end
 
+    context "when the Wiki for a project exists" do
+      before do
+        @wiki = ProjectWiki.new(project)
+        env["PATH_INFO"] = "#{@wiki.repository.path_with_namespace}.git/info/refs"
+        project.update_attribute(:visibility_level, Project::PUBLIC)
+      end
+
+      it "responds with the right project" do
+        response = auth.call(env)
+        json_body = ActiveSupport::JSON.decode(response[2][0])
+
+        expect(response.first).to eq(200)
+        expect(json_body['RepoPath']).to include(@wiki.repository.path_with_namespace)
+      end
+    end
+
     context "when the project exists" do
       before do
         env["PATH_INFO"] = project.path_with_namespace + ".git"
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 5d7ff4f612250f25320354b8b0bf86c314378000..21254f778d3f39798b1a88f276aea8f35e718926 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -140,28 +140,28 @@ describe Gitlab::ClosingIssueExtractor do
         message = "Closes #{reference} and fix #{reference2}"
 
         expect(subject.closed_by_message(message)).
-            to eq([issue, other_issue])
+            to match_array([issue, other_issue])
       end
 
       it 'fetches comma-separated issues references in single line message' do
         message = "Closes #{reference}, closes #{reference2}"
 
         expect(subject.closed_by_message(message)).
-            to eq([issue, other_issue])
+            to match_array([issue, other_issue])
       end
 
       it 'fetches comma-separated issues numbers in single line message' do
         message = "Closes #{reference}, #{reference2} and #{reference3}"
 
         expect(subject.closed_by_message(message)).
-            to eq([issue, other_issue, third_issue])
+            to match_array([issue, other_issue, third_issue])
       end
 
       it 'fetches issues in multi-line message' do
         message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
 
         expect(subject.closed_by_message(message)).
-            to eq([issue, other_issue])
+            to match_array([issue, other_issue])
       end
 
       it 'fetches issues in hybrid message' do
@@ -169,7 +169,7 @@ describe Gitlab::ClosingIssueExtractor do
                   "Also fixing issues #{reference2}, #{reference3} and #4"
 
         expect(subject.closed_by_message(message)).
-            to eq([issue, other_issue, third_issue])
+            to match_array([issue, other_issue, third_issue])
       end
     end
   end
diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb
index e8208e15e29ead60bb92b74ea82da81661dc40bf..8fb432367b63ddc603ca6b128f88537c7d6b9c7b 100644
--- a/spec/lib/gitlab/email/attachment_uploader_spec.rb
+++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb
@@ -13,7 +13,6 @@ describe Gitlab::Email::AttachmentUploader do
       expect(link).not_to be_nil
       expect(link[:is_image]).to be_truthy
       expect(link[:alt]).to eq("bricks")
-      expect(link[:url]).to include("/#{project.path_with_namespace}")
       expect(link[:url]).to include("bricks.png")
     end
   end
diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
index 3c6c84a0416aed1799818d493ea7b6a7b1def02b..e5b8d723fe5614652cc98000511f8250d554d1d7 100644
--- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
@@ -4,7 +4,7 @@ module Gitlab::Markdown
   describe CommitRangeReferenceFilter do
     include FilterSpecHelper
 
-    let(:project) { create(:project) }
+    let(:project) { create(:project, :public) }
     let(:commit1) { project.commit }
     let(:commit2) { project.commit("HEAD~2") }
 
@@ -75,12 +75,20 @@ module Gitlab::Markdown
         expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range'
       end
 
-      it 'includes a data-project-id attribute' do
+      it 'includes a data-project attribute' do
         doc = filter("See #{reference}")
         link = doc.css('a').first
 
-        expect(link).to have_attribute('data-project-id')
-        expect(link.attr('data-project-id')).to eq project.id.to_s
+        expect(link).to have_attribute('data-project')
+        expect(link.attr('data-project')).to eq project.id.to_s
+      end
+
+      it 'includes a data-commit-range attribute' do
+        doc = filter("See #{reference}")
+        link = doc.css('a').first
+
+        expect(link).to have_attribute('data-commit-range')
+        expect(link.attr('data-commit-range')).to eq range.to_reference
       end
 
       it 'supports an :only_path option' do
@@ -92,59 +100,45 @@ module Gitlab::Markdown
       end
 
       it 'adds to the results hash' do
-        result = pipeline_result("See #{reference}")
+        result = reference_pipeline_result("See #{reference}")
         expect(result[:references][:commit_range]).not_to be_empty
       end
     end
 
     context 'cross-project reference' do
       let(:namespace) { create(:namespace, name: 'cross-reference') }
-      let(:project2)  { create(:project, namespace: namespace) }
+      let(:project2)  { create(:project, :public, namespace: namespace) }
       let(:reference) { range.to_reference(project) }
 
       before do
         range.project = project2
       end
 
-      context 'when user can access reference' do
-        before { allow_cross_reference! }
-
-        it 'links to a valid reference' do
-          doc = filter("See #{reference}")
-
-          expect(doc.css('a').first.attr('href')).
-            to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
-        end
-
-        it 'links with adjacent text' do
-          doc = filter("Fixed (#{reference}.)")
-
-          exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}")
-          expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
-        end
+      it 'links to a valid reference' do
+        doc = filter("See #{reference}")
 
-        it 'ignores invalid commit IDs on the referenced project' do
-          exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
-          expect(filter(act).to_html).to eq exp
+        expect(doc.css('a').first.attr('href')).
+          to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+      end
 
-          exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
-          expect(filter(act).to_html).to eq exp
-        end
+      it 'links with adjacent text' do
+        doc = filter("Fixed (#{reference}.)")
 
-        it 'adds to the results hash' do
-          result = pipeline_result("See #{reference}")
-          expect(result[:references][:commit_range]).not_to be_empty
-        end
+        exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}")
+        expect(doc.to_html).to match(/\(<a.+>#{exp}<\/a>\.\)/)
       end
 
-      context 'when user cannot access reference' do
-        before { disallow_cross_reference! }
+      it 'ignores invalid commit IDs on the referenced project' do
+        exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}"
+        expect(filter(act).to_html).to eq exp
 
-        it 'ignores valid references' do
-          exp = act = "See #{reference}"
+        exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}"
+        expect(filter(act).to_html).to eq exp
+      end
 
-          expect(filter(act).to_html).to eq exp
-        end
+      it 'adds to the results hash' do
+        result = reference_pipeline_result("See #{reference}")
+        expect(result[:references][:commit_range]).not_to be_empty
       end
     end
   end
diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
index 9ed438252b3dbd6685c694b76a277fc5355ae923..d080efbf3d4fa011a7cea580601322c7120c2189 100644
--- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
@@ -4,7 +4,7 @@ module Gitlab::Markdown
   describe CommitReferenceFilter do
     include FilterSpecHelper
 
-    let(:project) { create(:project) }
+    let(:project) { create(:project, :public) }
     let(:commit)  { project.commit }
 
     it 'requires project context' do
@@ -71,12 +71,20 @@ module Gitlab::Markdown
         expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit'
       end
 
-      it 'includes a data-project-id attribute' do
+      it 'includes a data-project attribute' do
         doc = filter("See #{reference}")
         link = doc.css('a').first
 
-        expect(link).to have_attribute('data-project-id')
-        expect(link.attr('data-project-id')).to eq project.id.to_s
+        expect(link).to have_attribute('data-project')
+        expect(link.attr('data-project')).to eq project.id.to_s
+      end
+
+      it 'includes a data-commit attribute' do
+        doc = filter("See #{reference}")
+        link = doc.css('a').first
+
+        expect(link).to have_attribute('data-commit')
+        expect(link.attr('data-commit')).to eq commit.id
       end
 
       it 'supports an :only_path context' do
@@ -88,53 +96,39 @@ module Gitlab::Markdown
       end
 
       it 'adds to the results hash' do
-        result = pipeline_result("See #{reference}")
+        result = reference_pipeline_result("See #{reference}")
         expect(result[:references][:commit]).not_to be_empty
       end
     end
 
     context 'cross-project reference' do
       let(:namespace) { create(:namespace, name: 'cross-reference') }
-      let(:project2)  { create(:project, namespace: namespace) }
+      let(:project2)  { create(:project, :public, namespace: namespace) }
       let(:commit)    { project2.commit }
       let(:reference) { commit.to_reference(project) }
 
-      context 'when user can access reference' do
-        before { allow_cross_reference! }
-
-        it 'links to a valid reference' do
-          doc = filter("See #{reference}")
-
-          expect(doc.css('a').first.attr('href')).
-            to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
-        end
-
-        it 'links with adjacent text' do
-          doc = filter("Fixed (#{reference}.)")
+      it 'links to a valid reference' do
+        doc = filter("See #{reference}")
 
-          exp = Regexp.escape(project2.to_reference)
-          expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/)
-        end
+        expect(doc.css('a').first.attr('href')).
+          to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
+      end
 
-        it 'ignores invalid commit IDs on the referenced project' do
-          exp = act = "Committed #{invalidate_reference(reference)}"
-          expect(filter(act).to_html).to eq exp
-        end
+      it 'links with adjacent text' do
+        doc = filter("Fixed (#{reference}.)")
 
-        it 'adds to the results hash' do
-          result = pipeline_result("See #{reference}")
-          expect(result[:references][:commit]).not_to be_empty
-        end
+        exp = Regexp.escape(project2.to_reference)
+        expect(doc.to_html).to match(/\(<a.+>#{exp}@#{commit.short_id}<\/a>\.\)/)
       end
 
-      context 'when user cannot access reference' do
-        before { disallow_cross_reference! }
-
-        it 'ignores valid references' do
-          exp = act = "See #{reference}"
+      it 'ignores invalid commit IDs on the referenced project' do
+        exp = act = "Committed #{invalidate_reference(reference)}"
+        expect(filter(act).to_html).to eq exp
+      end
 
-          expect(filter(act).to_html).to eq exp
-        end
+      it 'adds to the results hash' do
+        result = reference_pipeline_result("See #{reference}")
+        expect(result[:references][:commit]).not_to be_empty
       end
     end
   end
diff --git a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb b/spec/lib/gitlab/markdown/cross_project_reference_spec.rb
index 4698d6138c20cc67e5261af8f09adc808524165b..8d4f9e403a603bfe37c1f6d22fa3df75f0e13dcf 100644
--- a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb
+++ b/spec/lib/gitlab/markdown/cross_project_reference_spec.rb
@@ -2,20 +2,16 @@ require 'spec_helper'
 
 module Gitlab::Markdown
   describe CrossProjectReference do
-    # context in the html-pipeline sense, not in the rspec sense
-    let(:context) do
-      {
-        current_user: double('user'),
-        project: double('project')
-      }
-    end
-
     include described_class
 
     describe '#project_from_ref' do
       context 'when no project was referenced' do
         it 'returns the project from context' do
-          expect(project_from_ref(nil)).to eq context[:project]
+          project = double
+
+          allow(self).to receive(:context).and_return({ project: project })
+
+          expect(project_from_ref(nil)).to eq project
         end
       end
 
@@ -26,29 +22,13 @@ module Gitlab::Markdown
       end
 
       context 'when referenced project exists' do
-        let(:project2) { double('referenced project') }
+        it 'returns the referenced project' do
+          project2 = double('referenced project')
 
-        before do
           expect(Project).to receive(:find_with_namespace).
             with('cross/reference').and_return(project2)
-        end
-
-        context 'and the user has permission to read it' do
-          it 'returns the referenced project' do
-            expect(self).to receive(:user_can_reference_project?).
-              with(project2).and_return(true)
-
-            expect(project_from_ref('cross/reference')).to eq project2
-          end
-        end
-
-        context 'and the user does not have permission to read it' do
-          it 'returns nil' do
-            expect(self).to receive(:user_can_reference_project?).
-              with(project2).and_return(false)
 
-            expect(project_from_ref('cross/reference')).to be_nil
-          end
+          expect(project_from_ref('cross/reference')).to eq project2
         end
       end
     end
diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
index 1dd54f58588747a4a926600501a8d58852be9990..94c80ae6611aeb2221ecabc6b57d6971de02887c 100644
--- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
@@ -8,7 +8,7 @@ module Gitlab::Markdown
       IssuesHelper
     end
 
-    let(:project) { create(:empty_project) }
+    let(:project) { create(:empty_project, :public) }
     let(:issue)   { create(:issue, project: project) }
 
     it 'requires project context' do
@@ -68,12 +68,20 @@ module Gitlab::Markdown
         expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue'
       end
 
-      it 'includes a data-project-id attribute' do
+      it 'includes a data-project attribute' do
         doc = filter("Issue #{reference}")
         link = doc.css('a').first
 
-        expect(link).to have_attribute('data-project-id')
-        expect(link.attr('data-project-id')).to eq project.id.to_s
+        expect(link).to have_attribute('data-project')
+        expect(link.attr('data-project')).to eq project.id.to_s
+      end
+
+      it 'includes a data-issue attribute' do
+        doc = filter("See #{reference}")
+        link = doc.css('a').first
+
+        expect(link).to have_attribute('data-issue')
+        expect(link.attr('data-issue')).to eq issue.id.to_s
       end
 
       it 'supports an :only_path context' do
@@ -85,60 +93,46 @@ module Gitlab::Markdown
       end
 
       it 'adds to the results hash' do
-        result = pipeline_result("Fixed #{reference}")
+        result = reference_pipeline_result("Fixed #{reference}")
         expect(result[:references][:issue]).to eq [issue]
       end
     end
 
     context 'cross-project reference' do
       let(:namespace) { create(:namespace, name: 'cross-reference') }
-      let(:project2)  { create(:empty_project, namespace: namespace) }
+      let(:project2)  { create(:empty_project, :public, namespace: namespace) }
       let(:issue)     { create(:issue, project: project2) }
       let(:reference) { issue.to_reference(project) }
 
-      context 'when user can access reference' do
-        before { allow_cross_reference! }
-
-        it 'ignores valid references when cross-reference project uses external tracker' do
-          expect_any_instance_of(Project).to receive(:get_issue).
-            with(issue.iid).and_return(nil)
-
-          exp = act = "Issue #{reference}"
-          expect(filter(act).to_html).to eq exp
-        end
+      it 'ignores valid references when cross-reference project uses external tracker' do
+        expect_any_instance_of(Project).to receive(:get_issue).
+          with(issue.iid).and_return(nil)
 
-        it 'links to a valid reference' do
-          doc = filter("See #{reference}")
-
-          expect(doc.css('a').first.attr('href')).
-            to eq helper.url_for_issue(issue.iid, project2)
-        end
-
-        it 'links with adjacent text' do
-          doc = filter("Fixed (#{reference}.)")
-          expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
-        end
+        exp = act = "Issue #{reference}"
+        expect(filter(act).to_html).to eq exp
+      end
 
-        it 'ignores invalid issue IDs on the referenced project' do
-          exp = act = "Fixed #{invalidate_reference(reference)}"
+      it 'links to a valid reference' do
+        doc = filter("See #{reference}")
 
-          expect(filter(act).to_html).to eq exp
-        end
+        expect(doc.css('a').first.attr('href')).
+          to eq helper.url_for_issue(issue.iid, project2)
+      end
 
-        it 'adds to the results hash' do
-          result = pipeline_result("Fixed #{reference}")
-          expect(result[:references][:issue]).to eq [issue]
-        end
+      it 'links with adjacent text' do
+        doc = filter("Fixed (#{reference}.)")
+        expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
       end
 
-      context 'when user cannot access reference' do
-        before { disallow_cross_reference! }
+      it 'ignores invalid issue IDs on the referenced project' do
+        exp = act = "Fixed #{invalidate_reference(reference)}"
 
-        it 'ignores valid references' do
-          exp = act = "See #{reference}"
+        expect(filter(act).to_html).to eq exp
+      end
 
-          expect(filter(act).to_html).to eq exp
-        end
+      it 'adds to the results hash' do
+        result = reference_pipeline_result("Fixed #{reference}")
+        expect(result[:references][:issue]).to eq [issue]
       end
     end
   end
diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
index e32089de376615e94eaabe7bbed84837d9440052..fc21b65a8437106d35f54ac484d0f4bdbda86cf8 100644
--- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
@@ -5,7 +5,7 @@ module Gitlab::Markdown
   describe LabelReferenceFilter do
     include FilterSpecHelper
 
-    let(:project)   { create(:empty_project) }
+    let(:project)   { create(:empty_project, :public) }
     let(:label)     { create(:label, project: project) }
     let(:reference) { label.to_reference }
 
@@ -25,12 +25,20 @@ module Gitlab::Markdown
       expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label'
     end
 
-    it 'includes a data-project-id attribute' do
+    it 'includes a data-project attribute' do
       doc = filter("Label #{reference}")
       link = doc.css('a').first
 
-      expect(link).to have_attribute('data-project-id')
-      expect(link.attr('data-project-id')).to eq project.id.to_s
+      expect(link).to have_attribute('data-project')
+      expect(link.attr('data-project')).to eq project.id.to_s
+    end
+
+    it 'includes a data-label attribute' do
+      doc = filter("See #{reference}")
+      link = doc.css('a').first
+
+      expect(link).to have_attribute('data-label')
+      expect(link.attr('data-label')).to eq label.id.to_s
     end
 
     it 'supports an :only_path context' do
@@ -42,7 +50,7 @@ module Gitlab::Markdown
     end
 
     it 'adds to the results hash' do
-      result = pipeline_result("Label #{reference}")
+      result = reference_pipeline_result("Label #{reference}")
       expect(result[:references][:label]).to eq [label]
     end
 
diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
index 66616b9336824b1fbc8aea063dc1cb3681b4b4de..3ef6cdfff33b11ca5f39a87c5f069608c497b904 100644
--- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
@@ -4,7 +4,7 @@ module Gitlab::Markdown
   describe MergeRequestReferenceFilter do
     include FilterSpecHelper
 
-    let(:project) { create(:project) }
+    let(:project) { create(:project, :public) }
     let(:merge)   { create(:merge_request, source_project: project) }
 
     it 'requires project context' do
@@ -56,12 +56,20 @@ module Gitlab::Markdown
         expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request'
       end
 
-      it 'includes a data-project-id attribute' do
+      it 'includes a data-project attribute' do
         doc = filter("Merge #{reference}")
         link = doc.css('a').first
 
-        expect(link).to have_attribute('data-project-id')
-        expect(link.attr('data-project-id')).to eq project.id.to_s
+        expect(link).to have_attribute('data-project')
+        expect(link.attr('data-project')).to eq project.id.to_s
+      end
+
+      it 'includes a data-merge-request attribute' do
+        doc = filter("See #{reference}")
+        link = doc.css('a').first
+
+        expect(link).to have_attribute('data-merge-request')
+        expect(link.attr('data-merge-request')).to eq merge.id.to_s
       end
 
       it 'supports an :only_path context' do
@@ -73,53 +81,39 @@ module Gitlab::Markdown
       end
 
       it 'adds to the results hash' do
-        result = pipeline_result("Merge #{reference}")
+        result = reference_pipeline_result("Merge #{reference}")
         expect(result[:references][:merge_request]).to eq [merge]
       end
     end
 
     context 'cross-project reference' do
       let(:namespace) { create(:namespace, name: 'cross-reference') }
-      let(:project2)  { create(:project, namespace: namespace) }
+      let(:project2)  { create(:project, :public, namespace: namespace) }
       let(:merge)     { create(:merge_request, source_project: project2) }
       let(:reference) { merge.to_reference(project) }
 
-      context 'when user can access reference' do
-        before { allow_cross_reference! }
-
-        it 'links to a valid reference' do
-          doc = filter("See #{reference}")
-
-          expect(doc.css('a').first.attr('href')).
-            to eq urls.namespace_project_merge_request_url(project2.namespace,
-                                                          project, merge)
-        end
-
-        it 'links with adjacent text' do
-          doc = filter("Merge (#{reference}.)")
-          expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
-        end
-
-        it 'ignores invalid merge IDs on the referenced project' do
-          exp = act = "Merge #{invalidate_reference(reference)}"
+      it 'links to a valid reference' do
+        doc = filter("See #{reference}")
 
-          expect(filter(act).to_html).to eq exp
-        end
+        expect(doc.css('a').first.attr('href')).
+          to eq urls.namespace_project_merge_request_url(project2.namespace,
+                                                        project, merge)
+      end
 
-        it 'adds to the results hash' do
-          result = pipeline_result("Merge #{reference}")
-          expect(result[:references][:merge_request]).to eq [merge]
-        end
+      it 'links with adjacent text' do
+        doc = filter("Merge (#{reference}.)")
+        expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
       end
 
-      context 'when user cannot access reference' do
-        before { disallow_cross_reference! }
+      it 'ignores invalid merge IDs on the referenced project' do
+        exp = act = "Merge #{invalidate_reference(reference)}"
 
-        it 'ignores valid references' do
-          exp = act = "See #{reference}"
+        expect(filter(act).to_html).to eq exp
+      end
 
-          expect(filter(act).to_html).to eq exp
-        end
+      it 'adds to the results hash' do
+        result = reference_pipeline_result("Merge #{reference}")
+        expect(result[:references][:merge_request]).to eq [merge]
       end
     end
   end
diff --git a/spec/lib/gitlab/markdown/redactor_filter_spec.rb b/spec/lib/gitlab/markdown/redactor_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..eea3f1cf370920b21d578a016a2a21fe5f3feea2
--- /dev/null
+++ b/spec/lib/gitlab/markdown/redactor_filter_spec.rb
@@ -0,0 +1,91 @@
+require 'spec_helper'
+
+module Gitlab::Markdown
+  describe RedactorFilter do
+    include ActionView::Helpers::UrlHelper
+    include FilterSpecHelper
+
+    it 'ignores non-GFM links' do
+      html = %(See <a href="https://google.com/">Google</a>)
+      doc = filter(html, current_user: double)
+
+      expect(doc.css('a').length).to eq 1
+    end
+
+    def reference_link(data)
+      link_to('text', '', class: 'gfm', data: data)
+    end
+
+    context 'with data-project' do
+      it 'removes unpermitted Project references' do
+        user = create(:user)
+        project = create(:empty_project)
+
+        link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name)
+        doc = filter(link, current_user: user)
+
+        expect(doc.css('a').length).to eq 0
+      end
+
+      it 'allows permitted Project references' do
+        user = create(:user)
+        project = create(:empty_project)
+        project.team << [user, :master]
+
+        link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name)
+        doc = filter(link, current_user: user)
+
+        expect(doc.css('a').length).to eq 1
+      end
+
+      it 'handles invalid Project references' do
+        link = reference_link(project: 12345, reference_filter: Gitlab::Markdown::ReferenceFilter.name)
+
+        expect { filter(link) }.not_to raise_error
+      end
+    end
+
+    context "for user references" do
+
+      context 'with data-group' do
+        it 'removes unpermitted Group references' do
+          user = create(:user)
+          group = create(:group)
+
+          link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
+          doc = filter(link, current_user: user)
+
+          expect(doc.css('a').length).to eq 0
+        end
+
+        it 'allows permitted Group references' do
+          user = create(:user)
+          group = create(:group)
+          group.add_developer(user)
+
+          link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
+          doc = filter(link, current_user: user)
+
+          expect(doc.css('a').length).to eq 1
+        end
+
+        it 'handles invalid Group references' do
+          link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
+
+          expect { filter(link) }.not_to raise_error
+        end
+      end
+
+      context 'with data-user' do
+        it 'allows any User reference' do
+          user = create(:user)
+
+          link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
+          doc = filter(link)
+
+          expect(doc.css('a').length).to eq 1
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb b/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4fa473ad191a0daad3bf517add4d43f16a974e73
--- /dev/null
+++ b/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+module Gitlab::Markdown
+  describe ReferenceGathererFilter do
+    include ActionView::Helpers::UrlHelper
+    include FilterSpecHelper
+
+    def reference_link(data)
+      link_to('text', '', class: 'gfm', data: data)
+    end
+
+    context "for issue references" do
+
+      context 'with data-project' do
+        it 'removes unpermitted Project references' do
+          user = create(:user)
+          project = create(:empty_project)
+          issue = create(:issue, project: project)
+
+          link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name)
+          result = pipeline_result(link, current_user: user)
+
+          expect(result[:references][:issue]).to be_empty
+        end
+
+        it 'allows permitted Project references' do
+          user = create(:user)
+          project = create(:empty_project)
+          issue = create(:issue, project: project)
+          project.team << [user, :master]
+
+          link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name)
+          result = pipeline_result(link, current_user: user)
+
+          expect(result[:references][:issue]).to eq([issue])
+        end
+
+        it 'handles invalid Project references' do
+          link = reference_link(project: 12345, issue: 12345, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name)
+
+          expect { pipeline_result(link) }.not_to raise_error
+        end
+      end
+    end
+
+    context "for user references" do
+
+      context 'with data-group' do
+        it 'removes unpermitted Group references' do
+          user = create(:user)
+          group = create(:group)
+
+          link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
+          result = pipeline_result(link, current_user: user)
+
+          expect(result[:references][:user]).to be_empty
+        end
+
+        it 'allows permitted Group references' do
+          user = create(:user)
+          group = create(:group)
+          group.add_developer(user)
+
+          link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
+          result = pipeline_result(link, current_user: user)
+
+          expect(result[:references][:user]).to eq([user])
+        end
+
+        it 'handles invalid Group references' do
+          link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
+
+          expect { pipeline_result(link) }.not_to raise_error
+        end
+      end
+
+      context 'with data-user' do
+        it 'allows any User reference' do
+          user = create(:user)
+
+          link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name)
+          result = pipeline_result(link)
+
+          expect(result[:references][:user]).to eq([user])
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
index e50c82d0b3c44fa38625edb59f8999e0f6024290..27cd00e80542a7da10c6e9394aafaffe7a3f863e 100644
--- a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb
@@ -44,7 +44,7 @@ module Gitlab::Markdown
         instance = described_class.new('Foo')
         3.times { instance.whitelist }
 
-        expect(instance.whitelist[:transformers].size).to eq 4
+        expect(instance.whitelist[:transformers].size).to eq 5
       end
 
       it 'allows syntax highlighting' do
@@ -77,19 +77,100 @@ module Gitlab::Markdown
       end
 
       it 'removes `rel` attribute from `a` elements' do
-        doc = filter(%q{<a href="#" rel="nofollow">Link</a>})
+        act = %q{<a href="#" rel="nofollow">Link</a>}
+        exp = %q{<a href="#">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
+        expect(filter(act).to_html).to eq exp
       end
 
-      it 'removes script-like `href` attribute from `a` elements' do
-        html = %q{<a href="javascript:alert('Hi')">Hi</a>}
-        doc = filter(html)
+      # Adapted from the Sanitize test suite: http://git.io/vczrM
+      protocols = {
+        'protocol-based JS injection: simple, no spaces' => {
+          input:  '<a href="javascript:alert(\'XSS\');">foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: simple, spaces before' => {
+          input:  '<a href="javascript    :alert(\'XSS\');">foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: simple, spaces after' => {
+          input:  '<a href="javascript:    alert(\'XSS\');">foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: simple, spaces before and after' => {
+          input:  '<a href="javascript    :   alert(\'XSS\');">foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: preceding colon' => {
+          input:  '<a href=":javascript:alert(\'XSS\');">foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: UTF-8 encoding' => {
+          input:  '<a href="javascript&#58;">foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: long UTF-8 encoding' => {
+          input:  '<a href="javascript&#0058;">foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: long UTF-8 encoding without semicolons' => {
+          input:  '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: hex encoding' => {
+          input:  '<a href="javascript&#x3A;">foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: long hex encoding' => {
+          input:  '<a href="javascript&#x003A;">foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: hex encoding without semicolons' => {
+          input:  '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
+          output: '<a>foo</a>'
+        },
+
+        'protocol-based JS injection: null char' => {
+          input:  "<a href=java\0script:alert(\"XSS\")>foo</a>",
+          output: '<a href="java"></a>'
+        },
+
+        'protocol-based JS injection: spaces and entities' => {
+          input:  '<a href=" &#14;  javascript:alert(\'XSS\');">foo</a>',
+          output: '<a href="">foo</a>'
+        },
+      }
+
+      protocols.each do |name, data|
+        it "handles #{name}" do
+          doc = filter(data[:input])
+
+          expect(doc.to_html).to eq data[:output]
+        end
+      end
+
+      it 'allows non-standard anchor schemes' do
+        exp = %q{<a href="irc://irc.freenode.net/git">IRC</a>}
+        act = filter(exp)
+
+        expect(act.to_html).to eq exp
+      end
+
+      it 'allows relative links' do
+        exp = %q{<a href="foo/bar.md">foo/bar.md</a>}
+        act = filter(exp)
 
-        expect(doc.css('a').size).to eq 1
-        expect(doc.at_css('a')['href']).to be_nil
+        expect(act.to_html).to eq exp
       end
     end
 
diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
index fd3f0d20fadc0a55595c25da03c8d79bc49a0283..9d9652dba46b1a3f0badd95de2cabdaa41cc6266 100644
--- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
@@ -4,7 +4,7 @@ module Gitlab::Markdown
   describe SnippetReferenceFilter do
     include FilterSpecHelper
 
-    let(:project)   { create(:empty_project) }
+    let(:project)   { create(:empty_project, :public) }
     let(:snippet)   { create(:project_snippet, project: project) }
     let(:reference) { snippet.to_reference }
 
@@ -55,12 +55,20 @@ module Gitlab::Markdown
         expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet'
       end
 
-      it 'includes a data-project-id attribute' do
+      it 'includes a data-project attribute' do
         doc = filter("Snippet #{reference}")
         link = doc.css('a').first
 
-        expect(link).to have_attribute('data-project-id')
-        expect(link.attr('data-project-id')).to eq project.id.to_s
+        expect(link).to have_attribute('data-project')
+        expect(link.attr('data-project')).to eq project.id.to_s
+      end
+
+      it 'includes a data-snippet attribute' do
+        doc = filter("See #{reference}")
+        link = doc.css('a').first
+
+        expect(link).to have_attribute('data-snippet')
+        expect(link.attr('data-snippet')).to eq snippet.id.to_s
       end
 
       it 'supports an :only_path context' do
@@ -72,52 +80,38 @@ module Gitlab::Markdown
       end
 
       it 'adds to the results hash' do
-        result = pipeline_result("Snippet #{reference}")
+        result = reference_pipeline_result("Snippet #{reference}")
         expect(result[:references][:snippet]).to eq [snippet]
       end
     end
 
     context 'cross-project reference' do
       let(:namespace) { create(:namespace, name: 'cross-reference') }
-      let(:project2)  { create(:empty_project, namespace: namespace) }
+      let(:project2)  { create(:empty_project, :public, namespace: namespace) }
       let(:snippet)   { create(:project_snippet, project: project2) }
       let(:reference) { snippet.to_reference(project) }
 
-      context 'when user can access reference' do
-        before { allow_cross_reference! }
-
-        it 'links to a valid reference' do
-          doc = filter("See #{reference}")
-
-          expect(doc.css('a').first.attr('href')).
-            to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
-        end
-
-        it 'links with adjacent text' do
-          doc = filter("See (#{reference}.)")
-          expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
-        end
-
-        it 'ignores invalid snippet IDs on the referenced project' do
-          exp = act = "See #{invalidate_reference(reference)}"
+      it 'links to a valid reference' do
+        doc = filter("See #{reference}")
 
-          expect(filter(act).to_html).to eq exp
-        end
+        expect(doc.css('a').first.attr('href')).
+          to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+      end
 
-        it 'adds to the results hash' do
-          result = pipeline_result("Snippet #{reference}")
-          expect(result[:references][:snippet]).to eq [snippet]
-        end
+      it 'links with adjacent text' do
+        doc = filter("See (#{reference}.)")
+        expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(reference)}<\/a>\.\)/)
       end
 
-      context 'when user cannot access reference' do
-        before { disallow_cross_reference! }
+      it 'ignores invalid snippet IDs on the referenced project' do
+        exp = act = "See #{invalidate_reference(reference)}"
 
-        it 'ignores valid references' do
-          exp = act = "See #{reference}"
+        expect(filter(act).to_html).to eq exp
+      end
 
-          expect(filter(act).to_html).to eq exp
-        end
+      it 'adds to the results hash' do
+        result = reference_pipeline_result("Snippet #{reference}")
+        expect(result[:references][:snippet]).to eq [snippet]
       end
     end
   end
diff --git a/spec/lib/gitlab/markdown/upload_link_filter_spec.rb b/spec/lib/gitlab/markdown/upload_link_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9ae45a6f5597e035e11e393b5424f34fca221ce9
--- /dev/null
+++ b/spec/lib/gitlab/markdown/upload_link_filter_spec.rb
@@ -0,0 +1,75 @@
+# encoding: UTF-8
+
+require 'spec_helper'
+
+module Gitlab::Markdown
+  describe UploadLinkFilter do
+    def filter(doc, contexts = {})
+      contexts.reverse_merge!({
+        project: project
+      })
+
+      described_class.call(doc, contexts)
+    end
+
+    def image(path)
+      %(<img src="#{path}" />)
+    end
+
+    def link(path)
+      %(<a href="#{path}">#{path}</a>)
+    end
+
+    let(:project) { create(:project) }
+
+    shared_examples :preserve_unchanged do
+      it 'does not modify any relative URL in anchor' do
+        doc = filter(link('README.md'))
+        expect(doc.at_css('a')['href']).to eq 'README.md'
+      end
+
+      it 'does not modify any relative URL in image' do
+        doc = filter(image('files/images/logo-black.png'))
+        expect(doc.at_css('img')['src']).to eq 'files/images/logo-black.png'
+      end
+    end
+
+    it 'does not raise an exception on invalid URIs' do
+      act = link("://foo")
+      expect { filter(act) }.not_to raise_error
+    end
+
+    context 'with a valid repository' do
+      it 'rebuilds relative URL for a link' do
+        doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+        expect(doc.at_css('a')['href']).
+          to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+      end
+
+      it 'rebuilds relative URL for an image' do
+        doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+        expect(doc.at_css('a')['href']).
+          to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+      end
+
+      it 'does not modify absolute URL' do
+        doc = filter(link('http://example.com'))
+        expect(doc.at_css('a')['href']).to eq 'http://example.com'
+      end
+
+      it 'supports Unicode filenames' do
+        path = '/uploads/한글.png'
+        escaped = Addressable::URI.escape(path)
+
+        # Stub these methods so the file doesn't actually need to be in the repo
+        allow_any_instance_of(described_class).
+          to receive(:file_exists?).and_return(true)
+        allow_any_instance_of(described_class).
+          to receive(:image?).with(path).and_return(true)
+
+        doc = filter(image(escaped))
+        expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/%ED%95%9C%EA%B8%80.png"
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
index b2155fab59b0099b5b5ed88fa03c18e1ab5d87eb..d9e0d7c42db7fd1bc83981d4b5bdeb0600596fbd 100644
--- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb
@@ -4,7 +4,7 @@ module Gitlab::Markdown
   describe UserReferenceFilter do
     include FilterSpecHelper
 
-    let(:project)   { create(:empty_project) }
+    let(:project)   { create(:empty_project, :public) }
     let(:user)      { create(:user) }
     let(:reference) { user.to_reference }
 
@@ -39,7 +39,7 @@ module Gitlab::Markdown
       end
 
       it 'adds to the results hash' do
-        result = pipeline_result("Hey #{reference}")
+        result = reference_pipeline_result("Hey #{reference}")
         expect(result[:references][:user]).to eq [project.creator]
       end
     end
@@ -64,59 +64,40 @@ module Gitlab::Markdown
         expect(doc.css('a').length).to eq 1
       end
 
-      it 'includes a data-user-id attribute' do
+      it 'includes a data-user attribute' do
         doc = filter("Hey #{reference}")
         link = doc.css('a').first
 
-        expect(link).to have_attribute('data-user-id')
-        expect(link.attr('data-user-id')).to eq user.namespace.owner_id.to_s
+        expect(link).to have_attribute('data-user')
+        expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s
       end
 
       it 'adds to the results hash' do
-        result = pipeline_result("Hey #{reference}")
+        result = reference_pipeline_result("Hey #{reference}")
         expect(result[:references][:user]).to eq [user]
       end
     end
 
     context 'mentioning a group' do
       let(:group)     { create(:group) }
-      let(:user)      { create(:user) }
       let(:reference) { group.to_reference }
 
-      context 'that the current user can read' do
-        before do
-          group.add_developer(user)
-        end
-
-        it 'links to the Group' do
-          doc = filter("Hey #{reference}", current_user: user)
-          expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
-        end
-
-        it 'includes a data-group-id attribute' do
-          doc = filter("Hey #{reference}", current_user: user)
-          link = doc.css('a').first
+      it 'links to the Group' do
+        doc = filter("Hey #{reference}")
+        expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
+      end
 
-          expect(link).to have_attribute('data-group-id')
-          expect(link.attr('data-group-id')).to eq group.id.to_s
-        end
+      it 'includes a data-group attribute' do
+        doc = filter("Hey #{reference}")
+        link = doc.css('a').first
 
-        it 'adds to the results hash' do
-          result = pipeline_result("Hey #{reference}", current_user: user)
-          expect(result[:references][:user]).to eq group.users
-        end
+        expect(link).to have_attribute('data-group')
+        expect(link.attr('data-group')).to eq group.id.to_s
       end
 
-      context 'that the current user cannot read' do
-        it 'ignores references to the Group' do
-          doc = filter("Hey #{reference}", current_user: user)
-          expect(doc.to_html).to eq "Hey #{reference}"
-        end
-
-        it 'does not add to the results hash' do
-          result = pipeline_result("Hey #{reference}", current_user: user)
-          expect(result[:references][:user]).to eq []
-        end
+      it 'adds to the results hash' do
+        result = reference_pipeline_result("Hey #{reference}")
+        expect(result[:references][:user]).to eq group.users
       end
     end
 
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 32a25f08cacdacf0bf7e33ecdfc3e66f7106ecc4..19327ac8ce030f2622e2a3ba7dac59b9c41cc430 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::ProjectSearchResults do
 
     it { expect(results.project).to eq(project) }
     it { expect(results.repository_ref).to be_nil }
-    it { expect(results.query).to eq('hello\\ world') }
+    it { expect(results.query).to eq('hello world') }
   end
 
   describe 'initialize with ref' do
@@ -18,6 +18,6 @@ describe Gitlab::ProjectSearchResults do
 
     it { expect(results.project).to eq(project) }
     it { expect(results.repository_ref).to eq(ref) }
-    it { expect(results.query).to eq('hello\\ world') }
+    it { expect(results.query).to eq('hello world') }
   end
 end
diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb
index 1b8ba7b4d4333d300f62845d8bfccaf1060ced1a..02710742625d18a20242d63b5f721a9b8c5ac73e 100644
--- a/spec/lib/gitlab/push_data_builder_spec.rb
+++ b/spec/lib/gitlab/push_data_builder_spec.rb
@@ -17,6 +17,9 @@ describe 'Gitlab::PushDataBuilder' do
     it { expect(data[:repository][:git_ssh_url]).to eq(project.ssh_url_to_repo) }
     it { expect(data[:repository][:visibility_level]).to eq(project.visibility_level) }
     it { expect(data[:total_commits_count]).to eq(3) }
+    it { expect(data[:added]).to eq(["gitlab-grack"]) }
+    it { expect(data[:modified]).to eq([".gitmodules", "files/ruby/popen.rb", "files/ruby/regex.rb"]) }
+    it { expect(data[:removed]).to eq([]) }
   end
 
   describe :build do
@@ -35,5 +38,8 @@ describe 'Gitlab::PushDataBuilder' do
     it { expect(data[:ref]).to eq('refs/tags/v1.1.0') }
     it { expect(data[:commits]).to be_empty }
     it { expect(data[:total_commits_count]).to be_zero }
+    it { expect(data[:added]).to eq([]) }
+    it { expect(data[:modified]).to eq([]) }
+    it { expect(data[:removed]).to eq([]) }
   end
 end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 088e34f050c317ab91272c69f0a6d63e5c3d4467..ad84d2274e804736329c47fd087f7e91144da7c9 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::ReferenceExtractor do
     project.team << [@u_bar, :guest]
 
     subject.analyze('@foo, @baduser, @bar, and @offteam')
-    expect(subject.users).to eq([@u_foo, @u_bar, @u_offteam])
+    expect(subject.users).to match_array([@u_foo, @u_bar, @u_offteam])
   end
 
   it 'ignores user mentions inside specific elements' do
@@ -37,7 +37,7 @@ describe Gitlab::ReferenceExtractor do
 
       > @offteam
     })
-    expect(subject.users).to eq([])
+    expect(subject.users).to match_array([])
   end
 
   it 'accesses valid issue objects' do
@@ -45,7 +45,7 @@ describe Gitlab::ReferenceExtractor do
     @i1 = create(:issue, project: project)
 
     subject.analyze("#{@i0.to_reference}, #{@i1.to_reference}, and #{Issue.reference_prefix}999.")
-    expect(subject.issues).to eq([@i0, @i1])
+    expect(subject.issues).to match_array([@i0, @i1])
   end
 
   it 'accesses valid merge requests' do
@@ -53,7 +53,7 @@ describe Gitlab::ReferenceExtractor do
     @m1 = create(:merge_request, source_project: project, target_project: project, source_branch: 'feature_conflict')
 
     subject.analyze("!999, !#{@m1.iid}, and !#{@m0.iid}.")
-    expect(subject.merge_requests).to eq([@m1, @m0])
+    expect(subject.merge_requests).to match_array([@m1, @m0])
   end
 
   it 'accesses valid labels' do
@@ -62,7 +62,7 @@ describe Gitlab::ReferenceExtractor do
     @l2 = create(:label)
 
     subject.analyze("~#{@l0.id}, ~999, ~#{@l2.id}, ~#{@l1.id}")
-    expect(subject.labels).to eq([@l0, @l1])
+    expect(subject.labels).to match_array([@l0, @l1])
   end
 
   it 'accesses valid snippets' do
@@ -71,7 +71,7 @@ describe Gitlab::ReferenceExtractor do
     @s2 = create(:project_snippet)
 
     subject.analyze("$#{@s0.id}, $999, $#{@s2.id}, $#{@s1.id}")
-    expect(subject.snippets).to eq([@s0, @s1])
+    expect(subject.snippets).to match_array([@s0, @s1])
   end
 
   it 'accesses valid commits' do
@@ -109,7 +109,7 @@ describe Gitlab::ReferenceExtractor do
       subject.analyze("this refers issue #{issue.to_reference(project)}")
       extracted = subject.issues
       expect(extracted.size).to eq(1)
-      expect(extracted).to eq([issue])
+      expect(extracted).to match_array([issue])
     end
   end
 end
diff --git a/spec/lib/gitlab/sherlock/collection_spec.rb b/spec/lib/gitlab/sherlock/collection_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a8a9d6fc7bc171f771893a605f193b11d6019140
--- /dev/null
+++ b/spec/lib/gitlab/sherlock/collection_spec.rb
@@ -0,0 +1,82 @@
+require 'spec_helper'
+
+describe Gitlab::Sherlock::Collection do
+  let(:collection) { described_class.new }
+
+  let(:transaction) do
+    Gitlab::Sherlock::Transaction.new('POST', '/cat_pictures')
+  end
+
+  describe '#add' do
+    it 'adds a new transaction' do
+      collection.add(transaction)
+
+      expect(collection).to_not be_empty
+    end
+
+    it 'is aliased as <<' do
+      collection << transaction
+
+      expect(collection).to_not be_empty
+    end
+  end
+
+  describe '#each' do
+    it 'iterates over every transaction' do
+      collection.add(transaction)
+
+      expect { |b| collection.each(&b) }.to yield_with_args(transaction)
+    end
+  end
+
+  describe '#clear' do
+    it 'removes all transactions' do
+      collection.add(transaction)
+
+      collection.clear
+
+      expect(collection).to be_empty
+    end
+  end
+
+  describe '#empty?' do
+    it 'returns true for an empty collection' do
+      expect(collection).to be_empty
+    end
+
+    it 'returns false for a collection with a transaction' do
+      collection.add(transaction)
+
+      expect(collection).to_not be_empty
+    end
+  end
+
+  describe '#find_transaction' do
+    it 'returns the transaction for the given ID' do
+      collection.add(transaction)
+
+      expect(collection.find_transaction(transaction.id)).to eq(transaction)
+    end
+
+    it 'returns nil when no transaction could be found' do
+      collection.add(transaction)
+
+      expect(collection.find_transaction('cats')).to be_nil
+    end
+  end
+
+  describe '#newest_first' do
+    it 'returns transactions sorted from new to old' do
+      trans1 = Gitlab::Sherlock::Transaction.new('POST', '/cat_pictures')
+      trans2 = Gitlab::Sherlock::Transaction.new('POST', '/more_cat_pictures')
+
+      allow(trans1).to receive(:finished_at).and_return(Time.utc(2015, 1, 1))
+      allow(trans2).to receive(:finished_at).and_return(Time.utc(2015, 1, 2))
+
+      collection.add(trans1)
+      collection.add(trans2)
+
+      expect(collection.newest_first).to eq([trans2, trans1])
+    end
+  end
+end
diff --git a/spec/lib/gitlab/sherlock/file_sample_spec.rb b/spec/lib/gitlab/sherlock/file_sample_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f05a59f56f6cb56a76ef3a74a09075861194ea49
--- /dev/null
+++ b/spec/lib/gitlab/sherlock/file_sample_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+describe Gitlab::Sherlock::FileSample do
+  let(:sample) { described_class.new(__FILE__, [], 150.4, 2) }
+
+  describe '#id' do
+    it 'returns the ID' do
+      expect(sample.id).to be_an_instance_of(String)
+    end
+  end
+
+  describe '#file' do
+    it 'returns the file path' do
+      expect(sample.file).to eq(__FILE__)
+    end
+  end
+
+  describe '#line_samples' do
+    it 'returns the line samples' do
+      expect(sample.line_samples).to eq([])
+    end
+  end
+
+  describe '#events' do
+    it 'returns the total number of events' do
+      expect(sample.events).to eq(2)
+    end
+  end
+
+  describe '#duration' do
+    it 'returns the total execution time' do
+      expect(sample.duration).to eq(150.4)
+    end
+  end
+
+  describe '#relative_path' do
+    it 'returns the relative path' do
+      expect(sample.relative_path).
+        to eq('spec/lib/gitlab/sherlock/file_sample_spec.rb')
+    end
+  end
+
+  describe '#to_param' do
+    it 'returns the sample ID' do
+      expect(sample.to_param).to eq(sample.id)
+    end
+  end
+
+  describe '#source' do
+    it 'returns the contents of the file' do
+      expect(sample.source).to eq(File.read(__FILE__))
+    end
+  end
+end
diff --git a/spec/lib/gitlab/sherlock/line_profiler_spec.rb b/spec/lib/gitlab/sherlock/line_profiler_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8f2e1299714ee17775b5b9f1d63d4508193a2660
--- /dev/null
+++ b/spec/lib/gitlab/sherlock/line_profiler_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+
+describe Gitlab::Sherlock::LineProfiler do
+  let(:profiler) { described_class.new }
+
+  describe '#profile' do
+    it 'runs the profiler when using MRI' do
+      allow(profiler).to receive(:mri?).and_return(true)
+      allow(profiler).to receive(:profile_mri)
+
+      profiler.profile { 'cats' }
+    end
+
+    it 'raises NotImplementedError when profiling an unsupported platform' do
+      allow(profiler).to receive(:mri?).and_return(false)
+
+      expect { profiler.profile { 'cats' } }.to raise_error(NotImplementedError)
+    end
+  end
+
+  describe '#profile_mri' do
+    it 'returns an Array containing the return value and profiling samples' do
+      allow(profiler).to receive(:lineprof).
+        and_yield.
+        and_return({ __FILE__ => [[0, 0, 0, 0]] })
+
+      retval, samples = profiler.profile_mri { 42 }
+
+      expect(retval).to eq(42)
+      expect(samples).to eq([])
+    end
+  end
+
+  describe '#aggregate_rblineprof' do
+    let(:raw_samples) do
+      { __FILE__ => [[30000, 30000, 5, 0], [15000, 15000, 4, 0]] }
+    end
+
+    it 'returns an Array of FileSample objects' do
+      samples = profiler.aggregate_rblineprof(raw_samples)
+
+      expect(samples).to be_an_instance_of(Array)
+      expect(samples[0]).to be_an_instance_of(Gitlab::Sherlock::FileSample)
+    end
+
+    describe 'the first FileSample object' do
+      let(:file_sample) do
+        profiler.aggregate_rblineprof(raw_samples)[0]
+      end
+
+      it 'uses the correct file path' do
+        expect(file_sample.file).to eq(__FILE__)
+      end
+
+      it 'contains a list of line samples' do
+        line_sample = file_sample.line_samples[0]
+
+        expect(line_sample).to be_an_instance_of(Gitlab::Sherlock::LineSample)
+
+        expect(line_sample.duration).to eq(15.0)
+        expect(line_sample.events).to eq(4)
+      end
+
+      it 'contains the total file execution time' do
+        expect(file_sample.duration).to eq(30.0)
+      end
+
+      it 'contains the total amount of file events' do
+        expect(file_sample.events).to eq(5)
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/sherlock/line_sample_spec.rb b/spec/lib/gitlab/sherlock/line_sample_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5f02f6a3213eb0b998f6b7c2edc6e5b78edbd2d2
--- /dev/null
+++ b/spec/lib/gitlab/sherlock/line_sample_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Gitlab::Sherlock::LineSample do
+  let(:sample) { described_class.new(150.0, 4) }
+
+  describe '#duration' do
+    it 'returns the duration' do
+      expect(sample.duration).to eq(150.0)
+    end
+  end
+
+  describe '#events' do
+    it 'returns the amount of events' do
+      expect(sample.events).to eq(4)
+    end
+  end
+
+  describe '#percentage_of' do
+    it 'returns the percentage of 1500.0' do
+      expect(sample.percentage_of(1500.0)).to be_within(0.1).of(10.0)
+    end
+  end
+
+  describe '#majority_of' do
+    it 'returns true if the sample takes up the majority of the given duration' do
+      expect(sample.majority_of?(500.0)).to eq(true)
+    end
+
+    it "returns false if the sample doesn't take up the majority of the given duration" do
+      expect(sample.majority_of?(1500.0)).to eq(false)
+    end
+  end
+end
diff --git a/spec/lib/gitlab/sherlock/location_spec.rb b/spec/lib/gitlab/sherlock/location_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b295a624b35e47c86e67ac37da61ddfb233f27dc
--- /dev/null
+++ b/spec/lib/gitlab/sherlock/location_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe Gitlab::Sherlock::Location do
+  let(:location) { described_class.new(__FILE__, 1) }
+
+  describe 'from_ruby_location' do
+    it 'creates a Location from a Thread::Backtrace::Location' do
+      input  = caller_locations[0]
+      output = described_class.from_ruby_location(input)
+
+      expect(output).to be_an_instance_of(described_class)
+      expect(output.path).to eq(input.path)
+      expect(output.line).to eq(input.lineno)
+    end
+  end
+
+  describe '#path' do
+    it 'returns the file path' do
+      expect(location.path).to eq(__FILE__)
+    end
+  end
+
+  describe '#line' do
+    it 'returns the line number' do
+      expect(location.line).to eq(1)
+    end
+  end
+
+  describe '#application?' do
+    it 'returns true for an application frame' do
+      expect(location.application?).to eq(true)
+    end
+
+    it 'returns false for a non application frame' do
+      loc = described_class.new('/tmp/cats.rb', 1)
+
+      expect(loc.application?).to eq(false)
+    end
+  end
+end
diff --git a/spec/lib/gitlab/sherlock/middleware_spec.rb b/spec/lib/gitlab/sherlock/middleware_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aa74fc53a7954dd6a2413eab494ec4eef64a8d25
--- /dev/null
+++ b/spec/lib/gitlab/sherlock/middleware_spec.rb
@@ -0,0 +1,79 @@
+require 'spec_helper'
+
+describe Gitlab::Sherlock::Middleware do
+  let(:app) { double(:app) }
+  let(:middleware) { described_class.new(app) }
+
+  describe '#call' do
+    describe 'when instrumentation is enabled' do
+      it 'instruments a request' do
+        allow(middleware).to receive(:instrument?).and_return(true)
+        allow(middleware).to receive(:call_with_instrumentation)
+
+        middleware.call({})
+      end
+    end
+
+    describe 'when instrumentation is disabled' do
+      it "doesn't instrument a request" do
+        allow(middleware).to receive(:instrument).and_return(false)
+        allow(app).to receive(:call)
+
+        middleware.call({})
+      end
+    end
+  end
+
+  describe '#call_with_instrumentation' do
+    it 'instruments a request' do
+      trans = double(:transaction)
+      retval = 'cats are amazing'
+      env = {}
+
+      allow(app).to receive(:call).with(env).and_return(retval)
+      allow(middleware).to receive(:transaction_from_env).and_return(trans)
+      allow(trans).to receive(:run).and_yield.and_return(retval)
+      allow(Gitlab::Sherlock.collection).to receive(:add).with(trans)
+
+      middleware.call_with_instrumentation(env)
+    end
+  end
+
+  describe '#instrument?' do
+    it 'returns false for a text/css request' do
+      env = { 'HTTP_ACCEPT' => 'text/css', 'REQUEST_URI' => '/' }
+
+      expect(middleware.instrument?(env)).to eq(false)
+    end
+
+    it 'returns false for a request to a Sherlock route' do
+      env = {
+        'HTTP_ACCEPT' => 'text/html',
+        'REQUEST_URI' => '/sherlock/transactions'
+      }
+
+      expect(middleware.instrument?(env)).to eq(false)
+    end
+
+    it 'returns true for a request that should be instrumented' do
+      env = {
+        'HTTP_ACCEPT' => 'text/html',
+        'REQUEST_URI' => '/cats'
+      }
+
+      expect(middleware.instrument?(env)).to eq(true)
+    end
+  end
+
+  describe '#transaction_from_env' do
+    it 'returns a Transaction' do
+      env = {
+        'HTTP_ACCEPT' => 'text/html',
+        'REQUEST_URI' => '/cats'
+      }
+
+      expect(middleware.transaction_from_env(env)).
+        to be_an_instance_of(Gitlab::Sherlock::Transaction)
+    end
+  end
+end
diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a9afef5dc1dc8a7ac579bb7eead3ac44d47b320e
--- /dev/null
+++ b/spec/lib/gitlab/sherlock/query_spec.rb
@@ -0,0 +1,113 @@
+require 'spec_helper'
+
+describe Gitlab::Sherlock::Query do
+  let(:started_at)  { Time.utc(2015, 1, 1) }
+  let(:finished_at) { started_at + 5 }
+
+  let(:query) do
+    described_class.new('SELECT COUNT(*) FROM users', started_at, finished_at)
+  end
+
+  describe 'new_with_bindings' do
+    it 'returns a Query' do
+      sql = 'SELECT COUNT(*) FROM users WHERE id = $1'
+      bindings = [[double(:column), 10]]
+
+      query = described_class.
+        new_with_bindings(sql, bindings, started_at, finished_at)
+
+      expect(query.query).to eq('SELECT COUNT(*) FROM users WHERE id = 10;')
+    end
+  end
+
+  describe '#id' do
+    it 'returns a String' do
+      expect(query.id).to be_an_instance_of(String)
+    end
+  end
+
+  describe '#query' do
+    it 'returns the query with a trailing semi-colon' do
+      expect(query.query).to eq('SELECT COUNT(*) FROM users;')
+    end
+  end
+
+  describe '#started_at' do
+    it 'returns the start time' do
+      expect(query.started_at).to eq(started_at)
+    end
+  end
+
+  describe '#finished_at' do
+    it 'returns the completion time' do
+      expect(query.finished_at).to eq(finished_at)
+    end
+  end
+
+  describe '#backtrace' do
+    it 'returns the backtrace' do
+      expect(query.backtrace).to be_an_instance_of(Array)
+    end
+  end
+
+  describe '#duration' do
+    it 'returns the duration in milliseconds' do
+      expect(query.duration).to be_within(0.1).of(5000.0)
+    end
+  end
+
+  describe '#to_param' do
+    it 'returns the query ID' do
+      expect(query.to_param).to eq(query.id)
+    end
+  end
+
+  describe '#formatted_query' do
+    it 'returns a formatted version of the query' do
+      expect(query.formatted_query).to eq(<<-EOF.strip)
+SELECT COUNT(*)
+FROM users;
+      EOF
+    end
+  end
+
+  describe '#last_application_frame' do
+    it 'returns the last application frame' do
+      frame = query.last_application_frame
+
+      expect(frame).to be_an_instance_of(Gitlab::Sherlock::Location)
+      expect(frame.path).to eq(__FILE__)
+    end
+  end
+
+  describe '#application_backtrace' do
+    it 'returns an Array of application frames' do
+      frames = query.application_backtrace
+
+      expect(frames).to be_an_instance_of(Array)
+      expect(frames).to_not be_empty
+
+      frames.each do |frame|
+        expect(frame.path).to start_with(Rails.root.to_s)
+      end
+    end
+  end
+
+  describe '#explain' do
+    it 'returns the query plan as a String' do
+      lines = [
+        ['Aggregate (cost=123 rows=1)'],
+        ['  -> Index Only Scan using index_cats_are_amazing']
+      ]
+
+      result = double(:result, values: lines)
+
+      allow(query).to receive(:raw_explain).and_return(result)
+
+      expect(query.explain).to eq(<<-EOF.strip)
+Aggregate (cost=123 rows=1)
+  -> Index Only Scan using index_cats_are_amazing
+      EOF
+    end
+  end
+end
diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bb49fb65cf8c6ec65bcb5f8a02abe5b17aed2f2b
--- /dev/null
+++ b/spec/lib/gitlab/sherlock/transaction_spec.rb
@@ -0,0 +1,222 @@
+require 'spec_helper'
+
+describe Gitlab::Sherlock::Transaction do
+  let(:transaction) { described_class.new('POST', '/cat_pictures') }
+
+  describe '#id' do
+    it 'returns the transaction ID' do
+      expect(transaction.id).to be_an_instance_of(String)
+    end
+  end
+
+  describe '#type' do
+    it 'returns the type' do
+      expect(transaction.type).to eq('POST')
+    end
+  end
+
+  describe '#path' do
+    it 'returns the path' do
+      expect(transaction.path).to eq('/cat_pictures')
+    end
+  end
+
+  describe '#queries' do
+    it 'returns an Array of queries' do
+      expect(transaction.queries).to be_an_instance_of(Array)
+    end
+  end
+
+  describe '#file_samples' do
+    it 'returns an Array of file samples' do
+      expect(transaction.file_samples).to be_an_instance_of(Array)
+    end
+  end
+
+  describe '#started_at' do
+    it 'returns the start time' do
+      allow(transaction).to receive(:profile_lines).and_yield
+
+      transaction.run { 'cats are amazing' }
+
+      expect(transaction.started_at).to be_an_instance_of(Time)
+    end
+  end
+
+  describe '#finished_at' do
+    it 'returns the completion time' do
+      allow(transaction).to receive(:profile_lines).and_yield
+
+      transaction.run { 'cats are amazing' }
+
+      expect(transaction.finished_at).to be_an_instance_of(Time)
+    end
+  end
+
+  describe '#view_counts' do
+    it 'returns a Hash' do
+      expect(transaction.view_counts).to be_an_instance_of(Hash)
+    end
+
+    it 'sets the default value of a key to 0' do
+      expect(transaction.view_counts['cats.rb']).to be_zero
+    end
+  end
+
+  describe '#run' do
+    it 'runs the transaction' do
+      allow(transaction).to receive(:profile_lines).and_yield
+
+      retval = transaction.run { 'cats are amazing' }
+
+      expect(retval).to eq('cats are amazing')
+    end
+  end
+
+  describe '#duration' do
+    it 'returns the duration in seconds' do
+      start_time = Time.now
+
+      allow(transaction).to receive(:started_at).and_return(start_time)
+      allow(transaction).to receive(:finished_at).and_return(start_time + 5)
+
+      expect(transaction.duration).to be_within(0.1).of(5.0)
+    end
+  end
+
+  describe '#to_param' do
+    it 'returns the transaction ID' do
+      expect(transaction.to_param).to eq(transaction.id)
+    end
+  end
+
+  describe '#sorted_queries' do
+    it 'returns the queries in descending order' do
+      start_time = Time.now
+
+      query1 = Gitlab::Sherlock::Query.new('SELECT 1', start_time, start_time)
+
+      query2 = Gitlab::Sherlock::Query.
+        new('SELECT 2', start_time, start_time + 5)
+
+      transaction.queries << query1
+      transaction.queries << query2
+
+      expect(transaction.sorted_queries).to eq([query2, query1])
+    end
+  end
+
+  describe '#sorted_file_samples' do
+    it 'returns the file samples in descending order' do
+      sample1 = Gitlab::Sherlock::FileSample.new(__FILE__, [], 10.0, 1)
+      sample2 = Gitlab::Sherlock::FileSample.new(__FILE__, [], 15.0, 1)
+
+      transaction.file_samples << sample1
+      transaction.file_samples << sample2
+
+      expect(transaction.sorted_file_samples).to eq([sample2, sample1])
+    end
+  end
+
+  describe '#find_query' do
+    it 'returns a Query when found' do
+      query = Gitlab::Sherlock::Query.new('SELECT 1', Time.now, Time.now)
+
+      transaction.queries << query
+
+      expect(transaction.find_query(query.id)).to eq(query)
+    end
+
+    it 'returns nil when no query could be found' do
+      expect(transaction.find_query('cats')).to be_nil
+    end
+  end
+
+  describe '#find_file_sample' do
+    it 'returns a FileSample when found' do
+      sample = Gitlab::Sherlock::FileSample.new(__FILE__, [], 10.0, 1)
+
+      transaction.file_samples << sample
+
+      expect(transaction.find_file_sample(sample.id)).to eq(sample)
+    end
+
+    it 'returns nil when no file sample could be found' do
+      expect(transaction.find_file_sample('cats')).to be_nil
+    end
+  end
+
+  describe '#profile_lines' do
+    describe 'when line profiling is enabled' do
+      it 'yields the block using the line profiler' do
+        allow(Gitlab::Sherlock).to receive(:enable_line_profiler?).
+          and_return(true)
+
+        allow_any_instance_of(Gitlab::Sherlock::LineProfiler).
+          to receive(:profile).and_return('cats are amazing', [])
+
+        retval = transaction.profile_lines { 'cats are amazing' }
+
+        expect(retval).to eq('cats are amazing')
+      end
+    end
+
+    describe 'when line profiling is disabled' do
+      it 'yields the block' do
+        allow(Gitlab::Sherlock).to receive(:enable_line_profiler?).
+          and_return(false)
+
+        retval = transaction.profile_lines { 'cats are amazing' }
+
+        expect(retval).to eq('cats are amazing')
+      end
+    end
+  end
+
+  describe '#subscribe_to_active_record' do
+    let(:subscription) { transaction.subscribe_to_active_record }
+    let(:time) { Time.now }
+    let(:query_data) { { sql: 'SELECT 1', binds: [] } }
+
+    after do
+      ActiveSupport::Notifications.unsubscribe(subscription)
+    end
+
+    it 'tracks executed queries' do
+      expect(transaction).to receive(:track_query).
+        with('SELECT 1', [], time, time)
+
+      subscription.publish('test', time, time, nil, query_data)
+    end
+
+    it 'only tracks queries triggered from the transaction thread' do
+      expect(transaction).to_not receive(:track_query)
+
+      Thread.new { subscription.publish('test', time, time, nil, query_data) }.
+        join
+    end
+  end
+
+  describe '#subscribe_to_action_view' do
+    let(:subscription) { transaction.subscribe_to_action_view }
+    let(:time) { Time.now }
+    let(:view_data) { { identifier: 'foo.rb' } }
+
+    after do
+      ActiveSupport::Notifications.unsubscribe(subscription)
+    end
+
+    it 'tracks rendered views' do
+      expect(transaction).to receive(:track_view).with('foo.rb')
+
+      subscription.publish('test', time, time, nil, view_data)
+    end
+
+    it 'only tracks views rendered from the transaction thread' do
+      expect(transaction).to_not receive(:track_view)
+
+      Thread.new { subscription.publish('test', time, time, nil, view_data) }.
+        join
+    end
+  end
+end
diff --git a/spec/lib/gitlab/uploads_transfer_spec.rb b/spec/lib/gitlab/uploads_transfer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..260364a513e4d2646213999645cd09511ba0b85d
--- /dev/null
+++ b/spec/lib/gitlab/uploads_transfer_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Gitlab::UploadsTransfer do
+  before do
+    @root_dir = File.join(Rails.root, "public", "uploads")
+    @upload_transfer = Gitlab::UploadsTransfer.new
+
+    @project_path_was = "test_project_was"
+    @project_path = "test_project"
+    @namespace_path_was = "test_namespace_was"
+    @namespace_path = "test_namespace"
+  end
+
+  after do
+    FileUtils.rm_rf([
+      File.join(@root_dir, @namespace_path),
+      File.join(@root_dir, @namespace_path_was)
+    ])
+  end
+
+  describe '#move_project' do
+    it "moves project upload to another namespace" do
+      FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path))
+      @upload_transfer.move_project(@project_path, @namespace_path_was, @namespace_path)
+
+      expected_path = File.join(@root_dir, @namespace_path, @project_path)
+      expect(Dir.exist?(expected_path)).to be_truthy
+    end
+  end
+
+  describe '#rename_project' do
+    it "renames project" do
+      FileUtils.mkdir_p(File.join(@root_dir, @namespace_path, @project_path_was))
+      @upload_transfer.rename_project(@project_path_was, @project_path, @namespace_path)
+
+      expected_path = File.join(@root_dir, @namespace_path, @project_path)
+      expect(Dir.exist?(expected_path)).to be_truthy
+    end
+  end
+
+  describe '#rename_namespace' do
+    it "renames namespace" do
+      FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path))
+      @upload_transfer.rename_namespace(@namespace_path_was, @namespace_path)
+
+      expected_path = File.join(@root_dir, @namespace_path, @project_path)
+      expect(Dir.exist?(expected_path)).to be_truthy
+    end
+  end
+end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index cb67ec95d57469d11f09eb9b7b28422a66c3cfe7..47863d54579e05959f2c5930d89bf2352d8f695e 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -468,7 +468,7 @@ describe Notify do
         subject { Notify.note_commit_email(recipient.id, note.id) }
 
         it_behaves_like 'a note email'
-        it_behaves_like 'an answer to an existing thread', 'commits'
+        it_behaves_like 'an answer to an existing thread', 'commit'
 
         it 'has the correct subject' do
           is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index de0b2ef4cda00c34a5ca1286affc3910290de31a..f01fe8bd3982e9c0418f74725a3be569b6edb2b6 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -28,11 +28,11 @@
 require 'spec_helper'
 
 describe ApplicationSetting, models: true do
-  it { expect(ApplicationSetting.create_from_defaults).to be_valid }
+  let(:setting) { ApplicationSetting.create_from_defaults }
 
-  context 'restricted signup domains' do
-    let(:setting) { ApplicationSetting.create_from_defaults }
+  it { expect(setting).to be_valid }
 
+  context 'restricted signup domains' do
     it 'set single domain' do
       setting.restricted_signup_domains_raw = 'example.com'
       expect(setting.restricted_signup_domains).to eq(['example.com'])
@@ -53,4 +53,26 @@ describe ApplicationSetting, models: true do
       expect(setting.restricted_signup_domains).to eq(['example.com', '*.example.com'])
     end
   end
+
+  context 'shared runners' do
+    let(:gl_project) { create(:empty_project) }
+
+    before do
+      allow_any_instance_of(Project).to receive(:current_application_settings).and_return(setting)
+    end
+
+    subject { gl_project.ensure_gitlab_ci_project.shared_runners_enabled }
+
+    context 'enabled' do
+      before { setting.update_attributes(shared_runners_enabled: true) }
+
+      it { is_expected.to be_truthy }
+    end
+
+    context 'disabled' do
+      before { setting.update_attributes(shared_runners_enabled: false) }
+
+      it { is_expected.to be_falsey }
+    end
+  end
 end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index d875015b9913b2c259a2dc49484a99f9c44e9222..7f5abb83ac2298813e596c95468be60fc16dbfc8 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -200,13 +200,34 @@ describe Ci::Build do
     context 'returns variables' do
       subject { build.variables }
 
-      let(:variables) do
+      let(:predefined_variables) do
+        [
+          { key: :CI_BUILD_NAME, value: 'test', public: true },
+          { key: :CI_BUILD_STAGE, value: 'stage', public: true },
+        ]
+      end
+
+      let(:yaml_variables) do
         [
           { key: :DB_NAME, value: 'postgres', public: true }
         ]
       end
 
-      it { is_expected.to eq(variables) }
+      before { build.update_attributes(stage: 'stage') }
+
+      it { is_expected.to eq(predefined_variables + yaml_variables) }
+
+      context 'for tag' do
+        let(:tag_variable) do
+          [
+            { key: :CI_BUILD_TAG, value: 'master', public: true }
+          ]
+        end
+
+        before { build.update_attributes(tag: true) }
+
+        it { is_expected.to eq(tag_variable + predefined_variables + yaml_variables) }
+      end
 
       context 'and secure variables' do
         let(:secure_variables) do
@@ -219,7 +240,7 @@ describe Ci::Build do
           build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
         end
 
-        it { is_expected.to eq(variables + secure_variables) }
+        it { is_expected.to eq(predefined_variables + yaml_variables + secure_variables) }
 
         context 'and trigger variables' do
           let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
@@ -229,12 +250,17 @@ describe Ci::Build do
               { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
             ]
           end
+          let(:predefined_trigger_variable) do
+            [
+              { key: :CI_BUILD_TRIGGERED, value: 'true', public: true }
+            ]
+          end
 
           before do
             build.trigger_request = trigger_request
           end
 
-          it { is_expected.to eq(variables + secure_variables + trigger_variables) }
+          it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) }
         end
       end
     end
@@ -273,4 +299,105 @@ describe Ci::Build do
       is_expected.to eq(['rec1', pusher_email])
     end
   end
+
+  describe :can_be_served? do
+    let(:runner) { FactoryGirl.create :ci_specific_runner }
+
+    before { build.project.runners << runner }
+
+    context 'runner without tags' do
+      it 'can handle builds without tags' do
+        expect(build.can_be_served?(runner)).to be_truthy
+      end
+
+      it 'cannot handle build with tags' do
+        build.tag_list = ['aa']
+        expect(build.can_be_served?(runner)).to be_falsey
+      end
+    end
+
+    context 'runner with tags' do
+      before { runner.tag_list = ['bb', 'cc'] }
+
+      it 'can handle builds without tags' do
+        expect(build.can_be_served?(runner)).to be_truthy
+      end
+
+      it 'can handle build with matching tags' do
+        build.tag_list = ['bb']
+        expect(build.can_be_served?(runner)).to be_truthy
+      end
+
+      it 'cannot handle build with not matching tags' do
+        build.tag_list = ['aa']
+        expect(build.can_be_served?(runner)).to be_falsey
+      end
+    end
+  end
+
+  describe :any_runners_online? do
+    subject { build.any_runners_online? }
+
+    context 'when no runners' do
+      it { is_expected.to be_falsey }
+    end
+
+    context 'if there are runner' do
+      let(:runner) { FactoryGirl.create :ci_specific_runner }
+
+      before do
+        build.project.runners << runner
+        runner.update_attributes(contacted_at: 1.second.ago)
+      end
+
+      it { is_expected.to be_truthy }
+
+      it 'that is inactive' do
+        runner.update_attributes(active: false)
+        is_expected.to be_falsey
+      end
+
+      it 'that is not online' do
+        runner.update_attributes(contacted_at: nil)
+        is_expected.to be_falsey
+      end
+
+      it 'that cannot handle build' do
+        expect_any_instance_of(Ci::Build).to receive(:can_be_served?).and_return(false)
+        is_expected.to be_falsey
+      end
+
+    end
+  end
+
+  describe :show_warning? do
+    subject { build.show_warning? }
+
+    %w(pending).each do |state|
+      context "if commit_status.status is #{state}" do
+        before { build.status = state }
+
+        it { is_expected.to be_truthy }
+
+        context "and there are specific runner" do
+          let(:runner) { FactoryGirl.create :ci_specific_runner, contacted_at: 1.second.ago }
+
+          before do
+            build.project.runners << runner
+            runner.save
+          end
+
+          it { is_expected.to be_falsey }
+        end
+      end
+    end
+
+    %w(success failed canceled running).each do |state|
+      context "if commit_status.status is #{state}" do
+        before { build.status = state }
+
+        it { is_expected.to be_falsey }
+      end
+    end
+  end
 end
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index 330971174fbb38c4afb11dcf080d8fd5f3478c77..44dbd083f064cc193c9e056977e0d9dcaf048310 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -32,6 +32,24 @@ describe Ci::Commit do
   it { is_expected.to respond_to :git_author_email }
   it { is_expected.to respond_to :short_sha }
 
+  describe :ordered do
+    let(:project) { FactoryGirl.create :empty_project }
+
+    it 'returns ordered list of commits' do
+      commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project
+      commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project
+      expect(project.ci_commits.ordered).to eq([commit2, commit1])
+    end
+
+    it 'returns commits ordered by committed_at and id, with nulls last' do
+      commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project
+      commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project
+      commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project
+      commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project
+      expect(project.ci_commits.ordered).to eq([commit2, commit4, commit3, commit1])
+    end
+  end
+
   describe :last_build do
     subject { commit.last_build }
     before do
@@ -143,28 +161,28 @@ describe Ci::Commit do
   end
 
   describe :create_builds do
-    let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
+    let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
 
     def create_builds(trigger_request = nil)
       commit.create_builds('master', false, nil, trigger_request)
     end
 
-    def create_next_builds(trigger_request = nil)
-      commit.create_next_builds('master', false, nil, trigger_request)
+    def create_next_builds
+      commit.create_next_builds(commit.builds.order(:id).last)
     end
 
     it 'creates builds' do
       expect(create_builds).to be_truthy
-      commit.builds.reload
-      expect(commit.builds.size).to eq(2)
+      commit.builds.update_all(status: "success")
+      expect(commit.builds.count(:all)).to eq(2)
 
       expect(create_next_builds).to be_truthy
-      commit.builds.reload
-      expect(commit.builds.size).to eq(4)
+      commit.builds.update_all(status: "success")
+      expect(commit.builds.count(:all)).to eq(4)
 
       expect(create_next_builds).to be_truthy
-      commit.builds.reload
-      expect(commit.builds.size).to eq(5)
+      commit.builds.update_all(status: "success")
+      expect(commit.builds.count(:all)).to eq(5)
 
       expect(create_next_builds).to be_falsey
     end
@@ -176,12 +194,12 @@ describe Ci::Commit do
 
       it 'creates builds' do
         expect(create_builds).to be_truthy
-        commit.builds.reload
-        expect(commit.builds.size).to eq(2)
+        commit.builds.update_all(status: "success")
+        expect(commit.builds.count(:all)).to eq(2)
 
         expect(create_develop_builds).to be_truthy
-        commit.builds.reload
-        expect(commit.builds.size).to eq(4)
+        commit.builds.update_all(status: "success")
+        expect(commit.builds.count(:all)).to eq(4)
         expect(commit.refs.size).to eq(2)
         expect(commit.builds.pluck(:name).uniq.size).to eq(2)
       end
@@ -193,28 +211,24 @@ describe Ci::Commit do
 
       it 'creates builds' do
         expect(create_builds(trigger_request)).to be_truthy
-        commit.builds.reload
-        expect(commit.builds.size).to eq(2)
+        expect(commit.builds.count(:all)).to eq(2)
       end
 
       it 'rebuilds commit' do
         expect(create_builds).to be_truthy
-        commit.builds.reload
-        expect(commit.builds.size).to eq(2)
+        expect(commit.builds.count(:all)).to eq(2)
 
         expect(create_builds(trigger_request)).to be_truthy
-        commit.builds.reload
-        expect(commit.builds.size).to eq(4)
+        expect(commit.builds.count(:all)).to eq(4)
       end
 
       it 'creates next builds' do
         expect(create_builds(trigger_request)).to be_truthy
-        commit.builds.reload
-        expect(commit.builds.size).to eq(2)
+        expect(commit.builds.count(:all)).to eq(2)
+        commit.builds.update_all(status: "success")
 
-        expect(create_next_builds(trigger_request)).to be_truthy
-        commit.builds.reload
-        expect(commit.builds.size).to eq(4)
+        expect(create_next_builds).to be_truthy
+        expect(commit.builds.count(:all)).to eq(4)
       end
 
       context 'for [ci skip]' do
@@ -224,7 +238,7 @@ describe Ci::Commit do
 
         it 'rebuilds commit' do
           expect(commit.status).to eq('skipped')
-          expect(create_builds(trigger_request)).to be_truthy
+          expect(create_builds).to be_truthy
 
           # since everything in Ci::Commit is cached we need to fetch a new object
           new_commit = Ci::Commit.find_by_id(commit.id)
@@ -232,6 +246,129 @@ describe Ci::Commit do
         end
       end
     end
+
+    context 'properly creates builds when "when" is defined' do
+      let(:yaml) do
+        {
+          stages: ["build", "test", "test_failure", "deploy", "cleanup"],
+          build: {
+            stage: "build",
+            script: "BUILD",
+          },
+          test: {
+            stage: "test",
+            script: "TEST",
+          },
+          test_failure: {
+            stage: "test_failure",
+            script: "ON test failure",
+            when: "on_failure",
+          },
+          deploy: {
+            stage: "deploy",
+            script: "PUBLISH",
+          },
+          cleanup: {
+            stage: "cleanup",
+            script: "TIDY UP",
+            when: "always",
+          }
+        }
+      end
+
+      before do
+        stub_ci_commit_yaml_file(YAML.dump(yaml))
+      end
+
+      it 'properly creates builds' do
+        expect(create_builds).to be_truthy
+        expect(commit.builds.pluck(:name)).to contain_exactly('build')
+        expect(commit.builds.pluck(:status)).to contain_exactly('pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending')
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success')
+        expect(commit.status).to eq('success')
+      end
+
+      it 'properly creates builds when test fails' do
+        expect(create_builds).to be_truthy
+        expect(commit.builds.pluck(:name)).to contain_exactly('build')
+        expect(commit.builds.pluck(:status)).to contain_exactly('pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending')
+        commit.builds.running_or_pending.each(&:drop)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success')
+        expect(commit.status).to eq('failed')
+      end
+
+      it 'properly creates builds when test and test_failure fails' do
+        expect(create_builds).to be_truthy
+        expect(commit.builds.pluck(:name)).to contain_exactly('build')
+        expect(commit.builds.pluck(:status)).to contain_exactly('pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending')
+        commit.builds.running_or_pending.each(&:drop)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending')
+        commit.builds.running_or_pending.each(&:drop)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success')
+        expect(commit.status).to eq('failed')
+      end
+
+      it 'properly creates builds when deploy fails' do
+        expect(create_builds).to be_truthy
+        expect(commit.builds.pluck(:name)).to contain_exactly('build')
+        expect(commit.builds.pluck(:status)).to contain_exactly('pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending')
+        commit.builds.running_or_pending.each(&:drop)
+
+        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup')
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'pending')
+        commit.builds.running_or_pending.each(&:success)
+
+        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success')
+        expect(commit.status).to eq('failed')
+      end
+    end
   end
 
   describe "#finished_at" do
@@ -281,59 +418,4 @@ describe Ci::Commit do
       expect(commit.coverage).to be_nil
     end
   end
-
-  describe :should_create_next_builds? do
-    before do
-      @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: 'success'
-      @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: 'failed'
-      @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: 'failed'
-      @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'success'
-    end
-
-    context 'for success' do
-      it 'to create if all succeeded' do
-        expect(commit.should_create_next_builds?(@build4)).to be_truthy
-      end
-    end
-
-    context 'for failed' do
-      before do
-        @build4.update_attributes(status: 'failed')
-      end
-
-      it 'to not create' do
-        expect(commit.should_create_next_builds?(@build4)).to be_falsey
-      end
-
-      context 'and ignore failures for current' do
-        before do
-          @build4.update_attributes(allow_failure: true)
-        end
-
-        it 'to create' do
-          expect(commit.should_create_next_builds?(@build4)).to be_truthy
-        end
-      end
-    end
-
-    context 'for running' do
-      before do
-        @build4.update_attributes(status: 'running')
-      end
-
-      it 'to not create' do
-        expect(commit.should_create_next_builds?(@build4)).to be_falsey
-      end
-    end
-
-    context 'for retried' do
-      before do
-        @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'failed'
-      end
-
-      it 'to not create' do
-        expect(commit.should_create_next_builds?(@build4)).to be_falsey
-      end
-    end
-  end
 end
diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb
index dec4720a711b74c3e6ad1f00da8694cd651997f4..490c6a679821b02ddd09c1b3c957cf0ef829ece1 100644
--- a/spec/models/ci/project_spec.rb
+++ b/spec/models/ci/project_spec.rb
@@ -131,24 +131,6 @@ describe Ci::Project do
     end
   end
 
-  describe 'ordered commits' do
-    let(:project) { FactoryGirl.create :empty_project }
-
-    it 'returns ordered list of commits' do
-      commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project
-      commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project
-      expect(project.ci_commits).to eq([commit2, commit1])
-    end
-
-    it 'returns commits ordered by committed_at and id, with nulls last' do
-      commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project
-      commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project
-      commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project
-      commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project
-      expect(project.ci_commits).to eq([commit2, commit4, commit3, commit1])
-    end
-  end
-
   context :valid_project do
     let(:commit) { FactoryGirl.create(:ci_commit) }
 
@@ -259,5 +241,18 @@ describe Ci::Project do
       FactoryGirl.create(:ci_shared_runner)
       expect(project.any_runners?).to be_falsey
     end
+
+    it "checks the presence of specific runner" do
+      project = FactoryGirl.create(:ci_project)
+      specific_runner = FactoryGirl.create(:ci_specific_runner)
+      project.runners << specific_runner
+      expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
+    end
+
+    it "checks the presence of shared runner" do
+      project = FactoryGirl.create(:ci_project, shared_runners_enabled: true)
+      shared_runner = FactoryGirl.create(:ci_shared_runner)
+      expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy
+    end
   end
 end
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index 757593a7ab8f090b80f9ad63cd8a926f84c5ae03..f8a51c29dc2c0216cd3187e2a5ec8514dd16902c 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -32,7 +32,7 @@ describe Ci::Runner do
     end
 
     it 'should return the token if the description is an empty string' do
-      runner = FactoryGirl.build(:ci_runner, description: '')
+      runner = FactoryGirl.build(:ci_runner, description: '', token: 'token')
       expect(runner.display_name).to eq runner.token
     end
   end
@@ -48,6 +48,71 @@ describe Ci::Runner do
     it { expect(shared_runner.only_for?(project)).to be_truthy }
   end
 
+  describe :online do
+    subject { Ci::Runner.online }
+
+    before do
+      @runner1 = FactoryGirl.create(:ci_shared_runner, contacted_at: 1.year.ago)
+      @runner2 = FactoryGirl.create(:ci_shared_runner, contacted_at: 1.second.ago)
+    end
+
+    it { is_expected.to eq([@runner2])}
+  end
+
+  describe :online? do
+    let(:runner) { FactoryGirl.create(:ci_shared_runner) }
+
+    subject { runner.online? }
+
+    context 'never contacted' do
+      before { runner.contacted_at = nil }
+
+      it { is_expected.to be_falsey }
+    end
+
+    context 'contacted long time ago time' do
+      before { runner.contacted_at = 1.year.ago }
+
+      it { is_expected.to be_falsey }
+    end
+
+    context 'contacted 1s ago' do
+      before { runner.contacted_at = 1.second.ago }
+
+      it { is_expected.to be_truthy }
+    end
+  end
+
+  describe :status do
+    let(:runner) { FactoryGirl.create(:ci_shared_runner, contacted_at: 1.second.ago) }
+
+    subject { runner.status }
+
+    context 'never connected' do
+      before { runner.contacted_at = nil }
+
+      it { is_expected.to eq(:not_connected) }
+    end
+
+    context 'contacted 1s ago' do
+      before { runner.contacted_at = 1.second.ago }
+
+      it { is_expected.to eq(:online) }
+    end
+
+    context 'contacted long time ago' do
+      before { runner.contacted_at = 1.year.ago }
+
+      it { is_expected.to eq(:offline) }
+    end
+
+    context 'inactive' do
+      before { runner.active = false }
+
+      it { is_expected.to eq(:paused) }
+    end
+  end
+
   describe "belongs_to_one_project?" do
     it "returns false if there are two projects runner assigned to" do
       runner = FactoryGirl.create(:ci_specific_runner)
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index e303a97e6b55edd290ced7c3b79f93f5d6934cfd..90be93249511f5b8eef45d4b5d65587758c37553 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -89,9 +89,9 @@ eos
   end
 
   it_behaves_like 'a mentionable' do
-    subject { commit }
+    subject { create(:project).commit }
 
-    let(:author) { create(:user, email: commit.author_email) }
+    let(:author) { create(:user, email: subject.author_email) }
     let(:backref_text) { "commit #{subject.id}" }
     let(:set_mentionable_text) do
       ->(txt) { allow(subject).to receive(:safe_message).and_return(txt) }
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 8f706f8934b419eaca5e269d16712fbcb7f85100..0f13c4410cdc482943c7d9e17c25b804e81acb57 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -68,7 +68,6 @@ describe Issue, "Issuable" do
     end
   end
 
-
   describe "#to_hook_data" do
     let(:hook_data) { issue.to_hook_data(user) }
 
diff --git a/spec/models/concerns/mentionable_spec.rb b/spec/models/concerns/mentionable_spec.rb
index 2d6fe003215599acaf050a959299ab5d42ceadf6..6179882e93591f4556ddc9062b5c3ebba2dd14c0 100644
--- a/spec/models/concerns/mentionable_spec.rb
+++ b/spec/models/concerns/mentionable_spec.rb
@@ -25,7 +25,7 @@ describe Issue, "Mentionable" do
     it 'correctly removes already-mentioned Commits' do
       expect(SystemNoteService).not_to receive(:cross_reference)
 
-      issue.create_cross_references!(project, author, [commit2])
+      issue.create_cross_references!(author, [commit2])
     end
   end
 
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 80638fc8db2216fff6c064ea96f83dad5186ca64..0f23e81ace9f3f1d8e23f3d6524c989b194ed68f 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -84,4 +84,23 @@ describe Group do
       expect(group.avatar_type).to eq(["only images allowed"])
     end
   end
+
+  describe "public_profile?" do
+    it "returns true for public group" do
+      group = create(:group, public: true)
+      expect(group.public_profile?).to be_truthy
+    end
+
+    it "returns true for non-public group with public project" do
+      group = create(:group)
+      create(:project, :public, group: group)
+      expect(group.public_profile?).to be_truthy
+    end
+
+    it "returns false for non-public group with no public projects" do
+      group = create(:group)
+      create(:project, group: group)
+      expect(group.public_profile?).to be_falsy
+    end
+  end
 end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index cf336d829579ee7c4477bf06b27a9497ae65479d..c9aa1b063c660cd04f8bd9ab6b0984c1df4052fb 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -68,8 +68,45 @@ describe Issue do
     end
   end
 
+  describe '#closed_by_merge_requests' do
+    let(:project) { create(:project) }
+    let(:issue)   { create(:issue, project: project, state: "opened")}
+    let(:closed_issue) { build(:issue, project: project, state: "closed")}
+
+    let(:mr) do
+      opts = {
+        title: 'Awesome merge_request',
+        description: "Fixes #{issue.to_reference}",
+        source_branch: 'feature',
+        target_branch: 'master'
+      }
+      MergeRequests::CreateService.new(project, project.owner, opts).execute
+    end
+
+    let(:closed_mr) do
+      opts = {
+        title: 'Awesome merge_request 2',
+        description: "Fixes #{issue.to_reference}",
+        source_branch: 'feature',
+        target_branch: 'master',
+        state: 'closed'
+      }
+      MergeRequests::CreateService.new(project, project.owner, opts).execute
+    end
+
+    it 'returns the merge request to close this issue' do
+      allow(mr).to receive(:closes_issue?).with(issue).and_return(true)
+
+      expect(issue.closed_by_merge_requests).to eq([mr])
+    end
+
+    it "returns an empty array when the current issue is closed already" do
+      expect(closed_issue.closed_by_merge_requests).to eq([])
+    end
+  end
+
   it_behaves_like 'an editable mentionable' do
-    subject { create(:issue, project: project) }
+    subject { create(:issue) }
 
     let(:backref_text) { "issue #{subject.to_reference}" }
     let(:set_mentionable_text) { ->(txt){ subject.description = txt } }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 6aaf1c036b05aa231ed9a7fdd9a64865b1f80d9a..eed2cbc54128104814664888ca94702bdbb07a73 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -79,6 +79,12 @@ describe MergeRequest do
       expect(merge_request.commits).not_to be_empty
       expect(merge_request.mr_and_commit_notes.count).to eq(2)
     end
+
+    it "should include notes for commits from target project as well" do
+      create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.target_project)
+      expect(merge_request.commits).not_to be_empty
+      expect(merge_request.mr_and_commit_notes.count).to eq(3)
+    end
   end
 
   describe '#is_being_reassigned?' do
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index c88d53496636049e269d7e5b96dec32be224493e..77c586273220685649cfe4f42a7604cf732b39b9 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -140,4 +140,32 @@ describe Milestone do
     end
   end
 
+  describe '#sort_issues' do
+    let(:milestone) { create(:milestone) }
+
+    let(:issue1) { create(:issue, milestone: milestone, position: 1) }
+    let(:issue2) { create(:issue, milestone: milestone, position: 2) }
+    let(:issue3) { create(:issue, milestone: milestone, position: 3) }
+    let(:issue4) { create(:issue, position: 42) }
+
+    it 'sorts the given issues' do
+      milestone.sort_issues([issue3.id, issue2.id, issue1.id])
+
+      issue1.reload
+      issue2.reload
+      issue3.reload
+
+      expect(issue1.position).to eq(3)
+      expect(issue2.position).to eq(2)
+      expect(issue3.position).to eq(1)
+    end
+
+    it 'ignores issues not part of the milestone' do
+      milestone.sort_issues([issue3.id, issue2.id, issue1.id, issue4.id])
+
+      issue4.reload
+
+      expect(issue4.position).to eq(42)
+    end
+  end
 end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 3a0b194ba1e236d49d4a87c54400d280a710c084..75564839dcfb6baac5c2b44357b1184cd5fc0b73 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -192,10 +192,9 @@ describe Note do
   end
 
   it_behaves_like 'an editable mentionable' do
-    subject { create :note, noteable: issue, project: project }
+    subject { create :note, noteable: issue, project: issue.project }
 
-    let(:project) { create(:project) }
-    let(:issue) { create :issue, project: project }
+    let(:issue) { create :issue }
     let(:backref_text) { issue.gfm_reference }
     let(:set_mentionable_text) { ->(txt) { subject.note = txt } }
   end
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
index f8a3493f52df63b0c050b7da3f6d2a936273c9a5..c34b2487ecfa6721dc88bb115d11f0cb991517c4 100644
--- a/spec/models/project_services/bamboo_service_spec.rb
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -30,27 +30,65 @@ describe BambooService, models: true do
     let(:user)    { create(:user) }
     let(:project) { create(:project) }
 
-    before do
-      @bamboo_service = BambooService.create(
-        project: create(:project),
-        properties: {
-          bamboo_url: 'http://gitlab.com',
-          username: 'mic',
-          password: "password"
-        }
-      )
-    end
+    context "when a password was previously set" do
+      before do
+        @bamboo_service = BambooService.create(
+          project: create(:project),
+          properties: {
+            bamboo_url: 'http://gitlab.com',
+            username: 'mic',
+            password: "password"
+          }
+        )
+      end
+  
+      it "reset password if url changed" do
+        @bamboo_service.bamboo_url = 'http://gitlab1.com'
+        @bamboo_service.save
+        expect(@bamboo_service.password).to be_nil
+      end
+  
+      it "does not reset password if username changed" do
+        @bamboo_service.username = "some_name"
+        @bamboo_service.save
+        expect(@bamboo_service.password).to eq("password")
+      end
+
+      it "does not reset password if new url is set together with password, even if it's the same password" do
+        @bamboo_service.bamboo_url = 'http://gitlab_edited.com'
+        @bamboo_service.password = 'password'
+        @bamboo_service.save
+        expect(@bamboo_service.password).to eq("password")
+        expect(@bamboo_service.bamboo_url).to eq("http://gitlab_edited.com")
+      end
 
-    it "reset password if url changed" do
-      @bamboo_service.bamboo_url = 'http://gitlab1.com'
-      @bamboo_service.save
-      expect(@bamboo_service.password).to be_nil
+      it "should reset password if url changed, even if setter called multiple times" do
+        @bamboo_service.bamboo_url = 'http://gitlab1.com'
+        @bamboo_service.bamboo_url = 'http://gitlab1.com'
+        @bamboo_service.save
+        expect(@bamboo_service.password).to be_nil
+      end
     end
+    
+    context "when no password was previously set" do
+      before do
+        @bamboo_service = BambooService.create(
+          project: create(:project),
+          properties: {
+            bamboo_url: 'http://gitlab.com',
+            username: 'mic'
+          }
+        )
+      end
+
+      it "saves password if new url is set together with password" do
+        @bamboo_service.bamboo_url = 'http://gitlab_edited.com'
+        @bamboo_service.password = 'password'
+        @bamboo_service.save
+        expect(@bamboo_service.password).to eq("password")
+        expect(@bamboo_service.bamboo_url).to eq("http://gitlab_edited.com")
+      end
 
-    it "does not reset password if username changed" do
-      @bamboo_service.username = "some_name"
-      @bamboo_service.save
-      expect(@bamboo_service.password).to eq("password")
     end
   end
 end
diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb
index 842089ebe0d66b9eae8fffda30c069838931b054..b9006b693b218075fd595035b151c325788a4d61 100644
--- a/spec/models/project_services/gitlab_ci_service_spec.rb
+++ b/spec/models/project_services/gitlab_ci_service_spec.rb
@@ -39,7 +39,7 @@ describe GitlabCiService do
     end
 
     describe :build_page do
-      it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/#{@ci_project.gl_project.path_with_namespace}/commit/2ab7834c/ci")}
+      it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/#{@ci_project.gl_project.path_with_namespace}/commit/2ab7834c/builds")}
     end
 
     describe "execute" do
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
index 3dbd2346bccd0eff9966e16382efa19f8f72f1ab..f26b47a856c5058091ba31184b1af6ca1ef2853f 100644
--- a/spec/models/project_services/teamcity_service_spec.rb
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -30,27 +30,64 @@ describe TeamcityService, models: true do
     let(:user)    { create(:user) }
     let(:project) { create(:project) }
 
-    before do
-      @teamcity_service = TeamcityService.create(
-        project: create(:project),
-        properties: {
-          teamcity_url: 'http://gitlab.com',
-          username: 'mic',
-          password: "password"
-        }
-      )
-    end
+    context "when a password was previously set" do
+      before do
+        @teamcity_service = TeamcityService.create(
+          project: create(:project),
+          properties: {
+            teamcity_url: 'http://gitlab.com',
+            username: 'mic',
+            password: "password"
+          }
+        )
+      end
+  
+      it "reset password if url changed" do
+        @teamcity_service.teamcity_url = 'http://gitlab1.com'
+        @teamcity_service.save
+        expect(@teamcity_service.password).to be_nil
+      end
+  
+      it "does not reset password if username changed" do
+        @teamcity_service.username = "some_name"
+        @teamcity_service.save
+        expect(@teamcity_service.password).to eq("password")
+      end
+
+      it "does not reset password if new url is set together with password, even if it's the same password" do
+        @teamcity_service.teamcity_url = 'http://gitlab_edited.com'
+        @teamcity_service.password = 'password'
+        @teamcity_service.save
+        expect(@teamcity_service.password).to eq("password")
+        expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com")
+      end
 
-    it "reset password if url changed" do
-      @teamcity_service.teamcity_url = 'http://gitlab1.com'
-      @teamcity_service.save
-      expect(@teamcity_service.password).to be_nil
+      it "should reset password if url changed, even if setter called multiple times" do
+        @teamcity_service.teamcity_url = 'http://gitlab1.com'
+        @teamcity_service.teamcity_url = 'http://gitlab1.com'
+        @teamcity_service.save
+        expect(@teamcity_service.password).to be_nil
+      end
     end
+    
+    context "when no password was previously set" do
+      before do
+        @teamcity_service = TeamcityService.create(
+          project: create(:project),
+          properties: {
+            teamcity_url: 'http://gitlab.com',
+            username: 'mic'
+          }
+        )
+      end
 
-    it "does not reset password if username changed" do
-      @teamcity_service.username = "some_name"
-      @teamcity_service.save
-      expect(@teamcity_service.password).to eq("password")
+      it "saves password if new url is set together with password" do
+        @teamcity_service.teamcity_url = 'http://gitlab_edited.com'
+        @teamcity_service.password = 'password'
+        @teamcity_service.save
+        expect(@teamcity_service.password).to eq("password")
+        expect(@teamcity_service.teamcity_url).to eq("http://gitlab_edited.com")
+      end
     end
   end
 end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 94802dcfb79f7486c7ca36ce4c618bac9aca611e..9f6cdeeaa962b1e0b8245d043628f4a65f085b18 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -223,7 +223,7 @@ describe ProjectWiki do
 
   def create_temp_repo(path)
     FileUtils.mkdir_p path
-    system(*%W(git init --quiet --bare -- #{path}))
+    system(*%W(#{Gitlab.config.git.bin_path} init --quiet --bare -- #{path}))
   end
 
   def remove_temp_repo(path)
diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..527005b2b698adae9c53e09ae84bcd81b6ebc18a
--- /dev/null
+++ b/spec/models/release_spec.rb
@@ -0,0 +1,16 @@
+require 'rails_helper'
+
+RSpec.describe Release, type: :model do
+  let(:release) { create(:release) }
+
+  it { expect(release).to be_valid }
+
+  describe 'associations' do
+    it { is_expected.to belong_to(:project) }
+  end
+
+  describe 'validation' do
+    it { is_expected.to validate_presence_of(:project) }
+    it { is_expected.to validate_presence_of(:description) }
+  end
+end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 60b939884761fee497898850f1d17cb91ca3ec6f..04437d64fee7e84b931337707ce94933c922b6f0 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -26,6 +26,15 @@ describe Repository do
     it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
   end
 
+  describe :find_commits_by_message do
+    subject { repository.find_commits_by_message('submodule').map{ |k| k.id } }
+
+    it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
+    it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
+    it { is_expected.to include('cfe32cf61b73a0d5e9f13e774abde7ff789b1660') }
+    it { is_expected.not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e') }
+  end
+
   describe :blob_at do
     context 'blank sha' do
       subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') }
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index da87ea5b84f175ce7835d6708021cf03558bf10d..692e5fda3ba9e7ead1d78e1b57bd42a6281f1b38 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -104,7 +104,7 @@ describe Service do
     end
   end
 
-  describe "#prop_updated?" do
+  describe "{property}_changed?" do
     let(:service) do
       BambooService.create(
         project: create(:project),
@@ -116,14 +116,112 @@ describe Service do
       )
     end
 
-    it "returns false" do
+    it "returns false when the property has not been assigned a new value" do
       service.username = "key_changed"
-      expect(service.prop_updated?(:bamboo_url)).to be_falsy
+      expect(service.bamboo_url_changed?).to be_falsy
     end
 
-    it "returns true" do
-      service.bamboo_url = "http://other.com"
-      expect(service.prop_updated?(:bamboo_url)).to be_truthy
+    it "returns true when the property has been assigned a different value" do
+      service.bamboo_url = "http://example.com"
+      expect(service.bamboo_url_changed?).to be_truthy
+    end
+
+    it "returns true when the property has been assigned a different value twice" do
+      service.bamboo_url = "http://example.com"
+      service.bamboo_url = "http://example.com"
+      expect(service.bamboo_url_changed?).to be_truthy
+    end
+
+    it "returns false when the property has been re-assigned the same value" do
+      service.bamboo_url = 'http://gitlab.com'
+      expect(service.bamboo_url_changed?).to be_falsy
+    end
+
+    it "returns false when the property has been assigned a new value then saved" do
+      service.bamboo_url = 'http://example.com'
+      service.save
+      expect(service.bamboo_url_changed?).to be_falsy
+    end
+  end
+
+  describe "{property}_touched?" do
+    let(:service) do
+      BambooService.create(
+        project: create(:project),
+        properties: {
+          bamboo_url: 'http://gitlab.com',
+          username: 'mic',
+          password: "password"
+        }
+      )
+    end
+
+    it "returns false when the property has not been assigned a new value" do
+      service.username = "key_changed"
+      expect(service.bamboo_url_touched?).to be_falsy
+    end
+
+    it "returns true when the property has been assigned a different value" do
+      service.bamboo_url = "http://example.com"
+      expect(service.bamboo_url_touched?).to be_truthy
+    end
+
+    it "returns true when the property has been assigned a different value twice" do
+      service.bamboo_url = "http://example.com"
+      service.bamboo_url = "http://example.com"
+      expect(service.bamboo_url_touched?).to be_truthy
+    end
+
+    it "returns true when the property has been re-assigned the same value" do
+      service.bamboo_url = 'http://gitlab.com'
+      expect(service.bamboo_url_touched?).to be_truthy
+    end
+
+    it "returns false when the property has been assigned a new value then saved" do
+      service.bamboo_url = 'http://example.com'
+      service.save
+      expect(service.bamboo_url_changed?).to be_falsy
+    end
+  end
+
+  describe "{property}_was" do
+    let(:service) do
+      BambooService.create(
+        project: create(:project),
+        properties: {
+          bamboo_url: 'http://gitlab.com',
+          username: 'mic',
+          password: "password"
+        }
+      )
+    end
+
+
+    it "returns nil when the property has not been assigned a new value" do
+      service.username = "key_changed"
+      expect(service.bamboo_url_was).to be_nil
+    end
+
+    it "returns the previous value when the property has been assigned a different value" do
+      service.bamboo_url = "http://example.com"
+      expect(service.bamboo_url_was).to eq('http://gitlab.com')
+    end
+
+    it "returns initial value when the property has been re-assigned the same value" do
+      service.bamboo_url = 'http://gitlab.com'
+      expect(service.bamboo_url_was).to eq('http://gitlab.com')
+    end
+
+    it "returns initial value when the property has been assigned multiple values" do
+      service.bamboo_url = "http://example.com"
+      service.bamboo_url = "http://example2.com"
+      expect(service.bamboo_url_was).to eq('http://gitlab.com')
+    end
+
+    it "returns nil when the property has been assigned a new value then saved" do
+      service.bamboo_url = 'http://example.com'
+      service.save
+      expect(service.bamboo_url_was).to be_nil
     end
   end
 end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index c71cfb3ebe341bde2f5364835130b74b34e5326c..49e0bfdd2ec91c8be9ab5f47b2e703e8406ebe0a 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -663,24 +663,24 @@ describe User do
       @user1 = create :user, created_at: Date.today - 1, last_sign_in_at: Date.today - 1, name: 'Omega'
     end
 
-    it "sorts users as recently_signed_in" do
+    it "sorts users by the recent sign-in time" do
       expect(User.sort('recent_sign_in').first).to eq(@user)
     end
 
-    it "sorts users as late_signed_in" do
+    it "sorts users by the oldest sign-in time" do
       expect(User.sort('oldest_sign_in').first).to eq(@user1)
     end
 
-    it "sorts users as recently_created" do
+    it "sorts users in descending order by their creation time" do
       expect(User.sort('created_desc').first).to eq(@user)
     end
 
-    it "sorts users as late_created" do
+    it "sorts users in ascending order by their creation time" do
       expect(User.sort('created_asc').first).to eq(@user1)
     end
 
-    it "sorts users by name when nil is passed" do
-      expect(User.sort(nil).first).to eq(@user)
+    it "sorts users by id in descending order when nil is passed" do
+      expect(User.sort(nil).first).to eq(@user1)
     end
   end
 
diff --git a/spec/requests/api/api_helpers_spec.rb b/spec/requests/api/api_helpers_spec.rb
index 4048c297013e0def9b08476a8e38b4ad1fc3171a..0c19094ec544adeee3c089697e9f0125db0dc7fa 100644
--- a/spec/requests/api/api_helpers_spec.rb
+++ b/spec/requests/api/api_helpers_spec.rb
@@ -1,7 +1,7 @@
 require 'spec_helper'
 
 describe API, api: true do
-  include API::APIHelpers
+  include API::Helpers
   include ApiHelpers
   let(:user) { create(:user) }
   let(:admin) { create(:admin) }
@@ -13,25 +13,25 @@ describe API, api: true do
   def set_env(token_usr, identifier)
     clear_env
     clear_param
-    env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = token_usr.private_token
-    env[API::APIHelpers::SUDO_HEADER] = identifier
+    env[API::Helpers::PRIVATE_TOKEN_HEADER] = token_usr.private_token
+    env[API::Helpers::SUDO_HEADER] = identifier
   end
 
   def set_param(token_usr, identifier)
     clear_env
     clear_param
-    params[API::APIHelpers::PRIVATE_TOKEN_PARAM] = token_usr.private_token
-    params[API::APIHelpers::SUDO_PARAM] = identifier
+    params[API::Helpers::PRIVATE_TOKEN_PARAM] = token_usr.private_token
+    params[API::Helpers::SUDO_PARAM] = identifier
   end
 
   def clear_env
-    env.delete(API::APIHelpers::PRIVATE_TOKEN_HEADER)
-    env.delete(API::APIHelpers::SUDO_HEADER)
+    env.delete(API::Helpers::PRIVATE_TOKEN_HEADER)
+    env.delete(API::Helpers::SUDO_HEADER)
   end
 
   def clear_param
-    params.delete(API::APIHelpers::PRIVATE_TOKEN_PARAM)
-    params.delete(API::APIHelpers::SUDO_PARAM)
+    params.delete(API::Helpers::PRIVATE_TOKEN_PARAM)
+    params.delete(API::Helpers::SUDO_PARAM)
   end
 
   def error!(message, status)
@@ -40,22 +40,22 @@ describe API, api: true do
 
   describe ".current_user" do
     it "should return nil for an invalid token" do
-      env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = 'invalid token'
+      env[API::Helpers::PRIVATE_TOKEN_HEADER] = 'invalid token'
       allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false }
       expect(current_user).to be_nil
     end
 
     it "should return nil for a user without access" do
-      env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token
+      env[API::Helpers::PRIVATE_TOKEN_HEADER] = user.private_token
       allow(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
       expect(current_user).to be_nil
     end
 
     it "should leave user as is when sudo not specified" do
-      env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token
+      env[API::Helpers::PRIVATE_TOKEN_HEADER] = user.private_token
       expect(current_user).to eq(user)
       clear_env
-      params[API::APIHelpers::PRIVATE_TOKEN_PARAM] = user.private_token
+      params[API::Helpers::PRIVATE_TOKEN_PARAM] = user.private_token
       expect(current_user).to eq(user)
     end
 
diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb
index b9e6dfc15a796c5ac5b6db2bd0c343ef11e5d594..a28607bd2400a1f7f8f5035b5547bb3bd0bf253a 100644
--- a/spec/requests/api/commit_status_spec.rb
+++ b/spec/requests/api/commit_status_spec.rb
@@ -18,7 +18,7 @@ describe API::API, api: true do
       before do
         @status1 = create(:commit_status, commit: ci_commit, status: 'running')
         @status2 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'pending')
-        @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running')
+        @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running', allow_failure: true)
         @status4 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'success')
         @status5 = create(:commit_status, commit: ci_commit, ref: 'develop', status: 'success')
         @status6 = create(:commit_status, commit: ci_commit, status: 'success')
@@ -30,6 +30,8 @@ describe API::API, api: true do
 
         expect(json_response).to be_an Array
         expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id)
+        json_response.sort_by!{ |status| status['id'] }
+        expect(json_response.map{ |status| status['allow_failure'] }).to eq([true, false, false, false])
       end
 
       it "should return all commit statuses" do
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 042e6352567101f76dca133a1da1c4b5fcda0b97..8efa09f75fd4ded6af034ada5266416df1af486b 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -19,6 +19,7 @@ describe API::API, api: true  do
       expect(response.status).to eq(200)
       expect(json_response['file_path']).to eq(file_path)
       expect(json_response['file_name']).to eq('popen.rb')
+      expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
       expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n")
     end
 
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 35b3d3e296abd08148dee80c60adfd20737ac42b..a68c7b1e461d3fbee2f5d3bcf7c95d15a56b430e 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -379,9 +379,14 @@ describe API::API, api: true  do
 
   describe "POST /projects/:id/merge_request/:merge_request_id/comments" do
     it "should return comment" do
+      original_count = merge_request.notes.size
+
       post api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user), note: "My comment"
       expect(response.status).to eq(201)
       expect(json_response['note']).to eq('My comment')
+      expect(json_response['author']['name']).to eq(user.name)
+      expect(json_response['author']['username']).to eq(user.username)
+      expect(merge_request.notes.size).to eq(original_count + 1)
     end
 
     it "should return 400 if note is missing" do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 580bbec77d18b93faed215a926a2b92c19723c8e..e9de9e0826dc109fd71b71a196a5c08e009e8ef8 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -606,28 +606,42 @@ describe API::API, api: true  do
 
     describe 'DELETE /projects/:id/fork' do
 
-      it "shouldn't available for non admin users" do
+      it "shouldn't be visible to users outside group" do
         delete api("/projects/#{project_fork_target.id}/fork", user)
-        expect(response.status).to eq(403)
+        expect(response.status).to eq(404)
       end
 
-      it 'should make forked project unforked' do
-        post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
-        project_fork_target.reload
-        expect(project_fork_target.forked_from_project).not_to be_nil
-        expect(project_fork_target.forked?).to be_truthy
-        delete api("/projects/#{project_fork_target.id}/fork", admin)
-        expect(response.status).to eq(200)
-        project_fork_target.reload
-        expect(project_fork_target.forked_from_project).to be_nil
-        expect(project_fork_target.forked?).not_to be_truthy
-      end
+      context 'when users belong to project group' do
+        let(:project_fork_target) { create(:project, group: create(:group)) }
 
-      it 'should be idempotent if not forked' do
-        expect(project_fork_target.forked_from_project).to be_nil
-        delete api("/projects/#{project_fork_target.id}/fork", admin)
-        expect(response.status).to eq(200)
-        expect(project_fork_target.reload.forked_from_project).to be_nil
+        before do
+          project_fork_target.group.add_owner user
+          project_fork_target.group.add_developer user2
+        end
+
+        it 'should be forbidden to non-owner users' do
+          delete api("/projects/#{project_fork_target.id}/fork", user2)
+          expect(response.status).to eq(403)
+        end
+
+        it 'should make forked project unforked' do
+          post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+          project_fork_target.reload
+          expect(project_fork_target.forked_from_project).not_to be_nil
+          expect(project_fork_target.forked?).to be_truthy
+          delete api("/projects/#{project_fork_target.id}/fork", admin)
+          expect(response.status).to eq(200)
+          project_fork_target.reload
+          expect(project_fork_target.forked_from_project).to be_nil
+          expect(project_fork_target.forked?).not_to be_truthy
+        end
+
+        it 'should be idempotent if not forked' do
+          expect(project_fork_target.forked_from_project).to be_nil
+          delete api("/projects/#{project_fork_target.id}/fork", admin)
+          expect(response.status).to eq(200)
+          expect(project_fork_target.reload.forked_from_project).to be_nil
+        end
       end
     end
   end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 09a79553f729cab2d5d53320f474c4ff6fb01b72..faf6b77a4629d20ea8dcb1bf84588f35bf4e8af2 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -36,8 +36,8 @@ describe API::API, api: true  do
       it 'should create a new annotated tag' do
         # Identity must be set in .gitconfig to create annotated tag.
         repo_path = project.repository.path_to_repo
-        system(*%W(git --git-dir=#{repo_path} config user.name #{user.name}))
-        system(*%W(git --git-dir=#{repo_path} config user.email #{user.email}))
+        system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name}))
+        system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email}))
 
         post api("/projects/#{project.id}/repository/tags", user),
              tag_name: 'v7.1.0',
@@ -166,24 +166,21 @@ describe API::API, api: true  do
       get api("/projects/#{project.id}/repository/archive", user)
       repo_name = project.repository.name.gsub("\.git", "")
       expect(response.status).to eq(200)
-      expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.tar.gz\"/)
-      expect(response.content_type).to eq(MIME::Types.type_for('file.tar.gz').first.content_type)
+      expect(json_response['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.gz/)
     end
 
     it "should get the archive.zip" do
       get api("/projects/#{project.id}/repository/archive.zip", user)
       repo_name = project.repository.name.gsub("\.git", "")
       expect(response.status).to eq(200)
-      expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.zip\"/)
-      expect(response.content_type).to eq(MIME::Types.type_for('file.zip').first.content_type)
+      expect(json_response['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.zip/)
     end
 
     it "should get the archive.tar.bz2" do
       get api("/projects/#{project.id}/repository/archive.tar.bz2", user)
       repo_name = project.repository.name.gsub("\.git", "")
       expect(response.status).to eq(200)
-      expect(response.headers['Content-Disposition']).to match(/filename\=\"#{repo_name}\-[^\.]+\.tar.bz2\"/)
-      expect(response.content_type).to eq(MIME::Types.type_for('file.tar.bz2').first.content_type)
+      expect(json_response['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.bz2/)
     end
 
     it "should return 404 for invalid sha" do
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 54c1d0199f6a88b57026a882ee6b42df3259f166..88218a93e1f589b90e015b20bde21c28fe9d59c4 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -76,6 +76,8 @@ describe Ci::API::API do
 
         expect(response.status).to eq(201)
         expect(json_response["variables"]).to eq([
+          { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
+          { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
           { "key" => "DB_NAME", "value" => "postgres", "public" => true },
           { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false },
         ])
@@ -93,6 +95,9 @@ describe Ci::API::API do
 
         expect(response.status).to eq(201)
         expect(json_response["variables"]).to eq([
+          { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
+          { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
+          { "key" => "CI_BUILD_TRIGGERED", "value" => "true", "public" => true },
           { "key" => "DB_NAME", "value" => "postgres", "public" => true },
           { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false },
           { "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false },
diff --git a/spec/services/archive_repository_service_spec.rb b/spec/services/archive_repository_service_spec.rb
index 0ec70c51b3a42d10b552bb7be52bacd7db131925..f7a36cd96705025a15ba2ce40b15a19f37cdc3fb 100644
--- a/spec/services/archive_repository_service_spec.rb
+++ b/spec/services/archive_repository_service_spec.rb
@@ -6,14 +6,14 @@ describe ArchiveRepositoryService do
 
   describe "#execute" do
     it "cleans old archives" do
-      expect(project.repository).to receive(:clean_old_archives)
+      expect(RepositoryArchiveCacheWorker).to receive(:perform_async)
 
       subject.execute(timeout: 0.0)
     end
 
     context "when the repository doesn't have an archive file path" do
       before do
-        allow(project.repository).to receive(:archive_file_path).and_return(nil)
+        allow(project.repository).to receive(:archive_metadata).and_return(Hash.new)
       end
 
       it "raises an error" do
@@ -21,70 +21,5 @@ describe ArchiveRepositoryService do
       end
     end
 
-    context "when the repository has an archive file path" do
-      let(:file_path)     { "/archive.zip" }
-      let(:pid_file_path) { "/archive.zip.pid" }
-
-      before do
-        allow(project.repository).to receive(:archive_file_path).and_return(file_path)
-        allow(project.repository).to receive(:archive_pid_file_path).and_return(pid_file_path)
-      end
-
-      context "when the archive file already exists" do
-        before do
-          allow(File).to receive(:exist?).with(file_path).and_return(true)
-        end
-
-        it "returns the file path" do
-          expect(subject.execute(timeout: 0.0)).to eq(file_path)
-        end
-      end
-
-      context "when the archive file doesn't exist yet" do
-        before do
-          allow(File).to receive(:exist?).with(file_path).and_return(false)
-          allow(File).to receive(:exist?).with(pid_file_path).and_return(true)
-        end
-
-        context "when the archive pid file doesn't exist yet" do
-          before do
-            allow(File).to receive(:exist?).with(pid_file_path).and_return(false)
-          end
-
-          it "queues the RepositoryArchiveWorker" do
-            expect(RepositoryArchiveWorker).to receive(:perform_async)
-
-            subject.execute(timeout: 0.0)
-          end
-        end
-
-        context "when the archive pid file already exists" do
-          it "doesn't queue the RepositoryArchiveWorker" do
-            expect(RepositoryArchiveWorker).not_to receive(:perform_async)
-
-            subject.execute(timeout: 0.0)
-          end
-        end
-
-        context "when the archive file exists after a little while" do
-          before do
-            Thread.new do
-              sleep 0.1
-              allow(File).to receive(:exist?).with(file_path).and_return(true)
-            end
-          end
-
-          it "returns the file path" do
-            expect(subject.execute(timeout: 0.2)).to eq(file_path)
-          end
-        end
-
-        context "when the archive file doesn't exist after the timeout" do
-          it "returns nil" do
-            expect(subject.execute(timeout: 0.0)).to eq(nil)
-          end
-        end
-      end
-    end
   end
 end
diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb
index d7242d684c63a9dad5ffbc7625f46f77970639ff..cda7d0c4a518da4fa9acc14d18ca2cb005e12412 100644
--- a/spec/services/ci/image_for_build_service_spec.rb
+++ b/spec/services/ci/image_for_build_service_spec.rb
@@ -4,8 +4,9 @@ module Ci
   describe ImageForBuildService do
     let(:service) { ImageForBuildService.new }
     let(:project) { FactoryGirl.create(:ci_project) }
-    let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
-    let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project, ref: 'master') }
+    let(:gl_project) { FactoryGirl.create(:project, gitlab_ci_project: project) }
+    let(:commit_sha) { gl_project.commit('master').sha }
+    let(:commit) { gl_project.ensure_ci_commit(commit_sha) }
     let(:build) { FactoryGirl.create(:ci_build, commit: commit) }
 
     describe :execute do
diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb
index 781764627aca0cb80274d093cc992d5f36f14388..b370dfbe1136ce891a01e90ad04c9a021f4cfa84 100644
--- a/spec/services/ci/register_build_service_spec.rb
+++ b/spec/services/ci/register_build_service_spec.rb
@@ -70,6 +70,10 @@ module Ci
       end
 
       context 'disallow shared runners' do
+        before do
+          gl_project.gitlab_ci_project.update(shared_runners_enabled: false)
+        end
+
         context 'shared runner' do
           let(:build) { service.execute(shared_runner) }
 
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index c483060fd73994235eb2c069bb936b79ce78ad60..17015d29e511c13a7cecc97f7a6b0bce7af62eee 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -112,6 +112,14 @@ describe GitPushService do
     it { expect(@event.project).to eq(project) }
     it { expect(@event.action).to eq(Event::PUSHED) }
     it { expect(@event.data).to eq(service.push_data) }
+
+    context "Updates merge requests" do
+      it "when pushing a new branch for the first time" do
+        expect(project).to receive(:update_merge_requests).
+                               with(@blankrev, 'newrev', 'refs/heads/master', user)
+        service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master')
+      end
+    end
   end
 
   describe "Web Hooks" do
@@ -155,7 +163,7 @@ describe GitPushService do
 
     before do
       allow(commit).to receive_messages(
-        safe_message: "this commit \n mentions ##{issue.id}",
+        safe_message: "this commit \n mentions #{issue.to_reference}",
         references: [issue],
         author_name: commit_author.name,
         author_email: commit_author.email
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 9516e7936d84dfa5b744fdc1076c35bf71477cc3..7ee4488521d4f39ba331d03295af31c19dda71ce 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -62,6 +62,25 @@ describe MergeRequests::RefreshService do
       it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
     end
 
+    context 'manual merge of source branch' do
+      before do
+        # Merge master -> feature branch
+        author = { email: 'test@gitlab.com', time: Time.now, name: "Me" }
+        commit_options = { message: 'Test message', committer: author, author: author }
+        master_commit = @project.repository.commit('master')
+        @project.repository.merge(@user, master_commit.id, 'feature', commit_options)
+        commit = @project.repository.commit('feature')
+        service.new(@project, @user).execute(@oldrev, commit.id, 'refs/heads/feature')
+        reload_mrs
+      end
+
+      it { expect(@merge_request.notes.last.note).to include('changed to merged') }
+      it { expect(@merge_request).to be_merged }
+      it { expect(@merge_request.diffs.length).to be > 0 }
+      it { expect(@fork_merge_request).to be_merged }
+      it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
+    end
+
     context 'push to fork repo source branch' do
       let(:refresh_service) { service.new(@fork_project, @user) }
       before do
@@ -106,6 +125,27 @@ describe MergeRequests::RefreshService do
       it { expect(@fork_merge_request.notes).to be_empty }
     end
 
+    context 'push new branch that exists in a merge request' do
+      let(:refresh_service) { service.new(@fork_project, @user) }
+
+      it 'refreshes the merge request' do
+        expect(refresh_service).to receive(:execute_hooks).
+                                       with(@fork_merge_request, 'update')
+        allow_any_instance_of(Repository).to receive(:merge_base).and_return(@oldrev)
+
+        refresh_service.execute(Gitlab::Git::BLANK_SHA, @newrev, 'refs/heads/master')
+        reload_mrs
+
+        expect(@merge_request.notes).to be_empty
+        expect(@merge_request).to be_open
+
+        notes = @fork_merge_request.notes.reorder(:created_at).map(&:note)
+        expect(notes[0]).to include('Restored source branch `master`')
+        expect(notes[1]).to include('Added 4 commits')
+        expect(@fork_merge_request).to be_open
+      end
+    end
+
     def reload_mrs
       @merge_request.reload
       @fork_merge_request.reload
diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb
index f12e09c58c36fb0f53bc9e8c0fb4c3e047bf8a82..ddee2e62dfcfed6e9289b4ef6be404566406f74a 100644
--- a/spec/services/projects/download_service_spec.rb
+++ b/spec/services/projects/download_service_spec.rb
@@ -37,7 +37,6 @@ describe Projects::DownloadService do
         it { expect(@link_to_file).to have_key('url') }
         it { expect(@link_to_file).to have_key('is_image') }
         it { expect(@link_to_file['is_image']).to be true }
-        it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") }
         it { expect(@link_to_file['url']).to match('rails_sample.jpg') }
         it { expect(@link_to_file['alt']).to eq('rails_sample') }
       end
@@ -52,7 +51,6 @@ describe Projects::DownloadService do
         it { expect(@link_to_file).to have_key('url') }
         it { expect(@link_to_file).to have_key('is_image') }
         it { expect(@link_to_file['is_image']).to be false }
-        it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") }
         it { expect(@link_to_file['url']).to match('doc_sample.txt') }
         it { expect(@link_to_file['alt']).to eq('doc_sample.txt') }
       end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index bb7da33b12e15f8eedc4bde0488268766bfacab1..47755bfc990311bdc420d1c0bf94d1fb75fab2d6 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -7,6 +7,8 @@ describe Projects::TransferService do
 
   context 'namespace -> namespace' do
     before do
+      allow_any_instance_of(Gitlab::UploadsTransfer).
+        to receive(:move_project).and_return(true)
       group.add_owner(user)
       @result = transfer_project(project, user, group)
     end
diff --git a/spec/services/projects/upload_service_spec.rb b/spec/services/projects/upload_service_spec.rb
index fa4ff6b01ad9993cfcc6fa2d1ed112e2dc08f347..1b1a80d1fe731931de7fabe31826750a5fb6b8d3 100644
--- a/spec/services/projects/upload_service_spec.rb
+++ b/spec/services/projects/upload_service_spec.rb
@@ -18,7 +18,6 @@ describe Projects::UploadService do
       it { expect(@link_to_file).to have_key(:is_image) }
       it { expect(@link_to_file).to have_value('banana_sample') }
       it { expect(@link_to_file[:is_image]).to equal(true) }
-      it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") }
       it { expect(@link_to_file[:url]).to match('banana_sample.gif') }
     end
 
@@ -34,7 +33,6 @@ describe Projects::UploadService do
       it { expect(@link_to_file).to have_value('dk') }
       it { expect(@link_to_file).to have_key(:is_image) }
       it { expect(@link_to_file[:is_image]).to equal(true) }
-      it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") }
       it { expect(@link_to_file[:url]).to match('dk.png') }
     end
 
@@ -49,7 +47,6 @@ describe Projects::UploadService do
       it { expect(@link_to_file).to have_key(:is_image) }
       it { expect(@link_to_file).to have_value('rails_sample') }
       it { expect(@link_to_file[:is_image]).to equal(true) }
-      it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") }
       it { expect(@link_to_file[:url]).to match('rails_sample.jpg') }
     end
 
@@ -64,7 +61,6 @@ describe Projects::UploadService do
       it { expect(@link_to_file).to have_key(:is_image) }
       it { expect(@link_to_file).to have_value('doc_sample.txt') }
       it { expect(@link_to_file[:is_image]).to equal(false) }
-      it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") }
       it { expect(@link_to_file[:url]).to match('doc_sample.txt') }
     end
 
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 2658576640c2f4f53caa0e73976ce79b3f636cd8..a45130bd473232cf9de5f426d15315b34d17481c 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -242,6 +242,18 @@ describe SystemNoteService do
     end
   end
 
+  describe '.change_branch_presence' do
+    subject { described_class.change_branch_presence(noteable, project, author, :source, 'feature', :delete) }
+
+    it_behaves_like 'a system note'
+
+    context 'when source branch deleted' do
+      it 'sets the note text' do
+        expect(subject.note).to eq "Deleted source branch `feature`"
+      end
+    end
+  end
+
   describe '.cross_reference' do
     subject { described_class.cross_reference(noteable, mentioner, author) }
 
diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb
index 203117aee7011dfbd9f01f63b4b544a577e5e22f..97e5c270a59b994222f05f7067702a9665bcec0a 100644
--- a/spec/support/filter_spec_helper.rb
+++ b/spec/support/filter_spec_helper.rb
@@ -29,12 +29,19 @@ module FilterSpecHelper
   #
   # Returns the Hash
   def pipeline_result(body, contexts = {})
-    contexts.reverse_merge!(project: project)
+    contexts.reverse_merge!(project: project) if defined?(project)
 
     pipeline = HTML::Pipeline.new([described_class], contexts)
     pipeline.call(body)
   end
 
+  def reference_pipeline_result(body, contexts = {})
+    contexts.reverse_merge!(project: project) if defined?(project)
+
+    pipeline = HTML::Pipeline.new([described_class, Gitlab::Markdown::ReferenceGathererFilter], contexts)
+    pipeline.call(body)
+  end
+
   # Modify a String reference to make it invalid
   #
   # Commit SHAs get reversed, IDs get incremented by 1, all other Strings get
@@ -55,20 +62,6 @@ module FilterSpecHelper
     end
   end
 
-  # Stub CrossProjectReference#user_can_reference_project? to return true for
-  # the current test
-  def allow_cross_reference!
-    allow_any_instance_of(described_class).
-      to receive(:user_can_reference_project?).and_return(true)
-  end
-
-  # Stub CrossProjectReference#user_can_reference_project? to return false for
-  # the current test
-  def disallow_cross_reference!
-    allow_any_instance_of(described_class).
-      to receive(:user_can_reference_project?).and_return(false)
-  end
-
   # Shortcut to Rails' auto-generated routes helpers, to avoid including the
   # module
   def urls
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
index 39a643914604076f99dfc11350ad8bed6c737325..bedc1a7f1db47960b8c8c53137782fdf9fbb5a5f 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/markdown_feature.rb
@@ -15,18 +15,17 @@ class MarkdownFeature
   end
 
   def group
-    unless @group
-      @group = create(:group)
-      @group.add_developer(user)
+    @group ||= create(:group).tap do |group|
+      group.add_developer(user)
     end
-
-    @group
   end
 
   # Direct references ----------------------------------------------------------
 
   def project
-    @project ||= create(:project)
+    @project ||= create(:project).tap do |project|
+      project.team << [user, :master]
+    end
   end
 
   def issue
@@ -46,12 +45,10 @@ class MarkdownFeature
   end
 
   def commit_range
-    unless @commit_range
+    @commit_range ||= begin
       commit2 = project.commit('HEAD~3')
-      @commit_range = CommitRange.new("#{commit.id}...#{commit2.id}", project)
+      CommitRange.new("#{commit.id}...#{commit2.id}", project)
     end
-
-    @commit_range
   end
 
   def simple_label
@@ -65,13 +62,12 @@ class MarkdownFeature
   # Cross-references -----------------------------------------------------------
 
   def xproject
-    unless @xproject
+    @xproject ||= begin
       namespace = create(:namespace, name: 'cross-reference')
-      @xproject = create(:project, namespace: namespace)
-      @xproject.team << [user, :developer]
+      create(:project, namespace: namespace) do |project|
+        project.team << [user, :developer]
+      end
     end
-
-    @xproject
   end
 
   def xissue
@@ -91,12 +87,10 @@ class MarkdownFeature
   end
 
   def xcommit_range
-    unless @xcommit_range
+    @xcommit_range ||= begin
       xcommit2 = xproject.commit('HEAD~2')
-      @xcommit_range = CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject)
+      CommitRange.new("#{xcommit.id}...#{xcommit2.id}", xproject)
     end
-
-    @xcommit_range
   end
 
   def raw_markdown
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index e3de0afb44825a6b84a7bb9723e22754dfb7538b..3bb568f4d494858c432f259cf37c20ef7465f535 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -5,7 +5,7 @@
 # - let(:set_mentionable_text) { lambda { |txt| "block that assigns txt to the subject's mentionable_text" } }
 
 def common_mentionable_setup
-  let(:project) { create :project }
+  let(:project) { subject.project }
   let(:author)  { subject.author }
 
   let(:mentioned_issue)  { create(:issue, project: project) }
@@ -50,6 +50,8 @@ def common_mentionable_setup
     }
     extra_commits.each { |c| commitmap[c.short_id] = c }
 
+    allow(Project).to receive(:find).and_call_original
+    allow(Project).to receive(:find).with(project.id.to_s).and_return(project)
     allow(project.repository).to receive(:commit) { |sha| commitmap[sha] }
 
     set_mentionable_text.call(ref_string)
@@ -65,7 +67,7 @@ shared_examples 'a mentionable' do
 
   it "extracts references from its reference property" do
     # De-duplicate and omit itself
-    refs = subject.references(project)
+    refs = subject.referenced_mentionables
     expect(refs.size).to eq(6)
     expect(refs).to include(mentioned_issue)
     expect(refs).to include(mentioned_mr)
@@ -84,14 +86,7 @@ shared_examples 'a mentionable' do
         with(referenced, subject.local_reference, author)
     end
 
-    subject.create_cross_references!(project, author)
-  end
-
-  it 'detects existing cross-references' do
-    SystemNoteService.cross_reference(mentioned_issue, subject.local_reference, author)
-
-    expect(subject).to have_mentioned(mentioned_issue)
-    expect(subject).not_to have_mentioned(mentioned_mr)
+    subject.create_cross_references!
   end
 end
 
@@ -143,6 +138,6 @@ shared_examples 'an editable mentionable' do
     end
 
     set_mentionable_text.call(new_text)
-    subject.create_new_cross_references!(project, author)
+    subject.create_new_cross_references!(author)
   end
 end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 3eab74ba9860be6207a74236e9cf4c91e6afcc74..787670e929792e5080975af31f4b5dc50dd96cf0 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -9,7 +9,7 @@ module TestEnv
     'flatten-dir'      => 'e56497b',
     'feature'          => '0b4bc9a',
     'feature_conflict' => 'bb5206f',
-    'fix'              => '12d65c8',
+    'fix'              => '48f0be4',
     'improve/awesome'  => '5937ac0',
     'markdown'         => '0ed8c6c',
     'master'           => '5937ac0',
@@ -96,15 +96,15 @@ module TestEnv
     clone_url = "https://gitlab.com/gitlab-org/#{repo_name}.git"
 
     unless File.directory?(repo_path)
-      system(*%W(git clone -q #{clone_url} #{repo_path}))
+      system(*%W(#{Gitlab.config.git.bin_path} clone -q #{clone_url} #{repo_path}))
     end
 
     Dir.chdir(repo_path) do
       branch_sha.each do |branch, sha|
         # Try to reset without fetching to avoid using the network.
-        reset = %W(git update-ref refs/heads/#{branch} #{sha})
+        reset = %W(#{Gitlab.config.git.bin_path} update-ref refs/heads/#{branch} #{sha})
         unless system(*reset)
-          if system(*%w(git fetch origin))
+          if system(*%W(#{Gitlab.config.git.bin_path} fetch origin))
             unless system(*reset)
               raise 'The fetched test seed '\
               'does not contain the required revision.'
@@ -117,7 +117,7 @@ module TestEnv
     end
 
     # We must copy bare repositories because we will push to them.
-    system(git_env, *%W(git clone -q --bare #{repo_path} #{repo_path_bare}))
+    system(git_env, *%W(#{Gitlab.config.git.bin_path} clone -q --bare #{repo_path} #{repo_path_bare}))
   end
 
   def copy_repo(project)
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 3be7dd4e52b59b10e03335116f86b267ffdbcceb..386ac9c8372007aec9104028445c94a4c016854d 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -55,6 +55,7 @@ describe 'gitlab:app namespace rake task' do
         expect(Rake::Task["gitlab:backup:db:restore"]).to receive(:invoke)
         expect(Rake::Task["gitlab:backup:repo:restore"]).to receive(:invoke)
         expect(Rake::Task["gitlab:backup:builds:restore"]).to receive(:invoke)
+        expect(Rake::Task["gitlab:backup:uploads:restore"]).to receive(:invoke)
         expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke)
         expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
       end
@@ -112,14 +113,14 @@ describe 'gitlab:app namespace rake task' do
 
     it 'should set correct permissions on the tar contents' do
       tar_contents, exit_status = Gitlab::Popen.popen(
-        %W{tar -tvf #{@backup_tar} db uploads repositories builds}
+        %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz}
       )
       expect(exit_status).to eq(0)
       expect(tar_contents).to match('db/')
-      expect(tar_contents).to match('uploads/')
+      expect(tar_contents).to match('uploads.tar.gz')
       expect(tar_contents).to match('repositories/')
-      expect(tar_contents).to match('builds/')
-      expect(tar_contents).not_to match(/^.{4,9}[rwx].* (db|uploads|repositories|builds)\/$/)
+      expect(tar_contents).to match('builds.tar.gz')
+      expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz)\/$/)
     end
 
     it 'should delete temp directories' do
@@ -160,12 +161,12 @@ describe 'gitlab:app namespace rake task' do
 
     it "does not contain skipped item" do
       tar_contents, _exit_status = Gitlab::Popen.popen(
-        %W{tar -tvf #{@backup_tar} db uploads repositories builds}
+        %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz}
       )
 
       expect(tar_contents).to match('db/')
-      expect(tar_contents).to match('uploads/')
-      expect(tar_contents).to match('builds/')
+      expect(tar_contents).to match('uploads.tar.gz')
+      expect(tar_contents).to match('builds.tar.gz')
       expect(tar_contents).not_to match('repositories/')
     end
 
diff --git a/spec/workers/repository_archive_worker_spec.rb b/spec/workers/repository_archive_worker_spec.rb
deleted file mode 100644
index a914d0ac8dcff64d8da122480c7d4055bd8d310a..0000000000000000000000000000000000000000
--- a/spec/workers/repository_archive_worker_spec.rb
+++ /dev/null
@@ -1,79 +0,0 @@
-require 'spec_helper'
-
-describe RepositoryArchiveWorker do
-  let(:project) { create(:project) }
-  subject { RepositoryArchiveWorker.new }
-
-  before do
-    allow(Project).to receive(:find).and_return(project)
-  end
-
-  describe "#perform" do
-    it "cleans old archives" do
-      expect(project.repository).to receive(:clean_old_archives)
-
-      subject.perform(project.id, "master", "zip")
-    end
-
-    context "when the repository doesn't have an archive file path" do
-      before do
-        allow(project.repository).to receive(:archive_file_path).and_return(nil)
-      end
-
-      it "doesn't archive the repo" do
-        expect(project.repository).not_to receive(:archive_repo)
-
-        subject.perform(project.id, "master", "zip")
-      end
-    end
-
-    context "when the repository has an archive file path" do
-      let(:file_path)     { "/archive.zip" }
-      let(:pid_file_path) { "/archive.zip.pid" }
-
-      before do
-        allow(project.repository).to receive(:archive_file_path).and_return(file_path)
-        allow(project.repository).to receive(:archive_pid_file_path).and_return(pid_file_path)
-      end
-
-      context "when the archive file already exists" do
-        before do
-          allow(File).to receive(:exist?).with(file_path).and_return(true)
-        end
-
-        it "doesn't archive the repo" do
-          expect(project.repository).not_to receive(:archive_repo)
-
-          subject.perform(project.id, "master", "zip")
-        end
-      end
-
-      context "when the archive file doesn't exist yet" do
-        before do
-          allow(File).to receive(:exist?).with(file_path).and_return(false)
-          allow(File).to receive(:exist?).with(pid_file_path).and_return(true)
-        end
-
-        context "when the archive pid file doesn't exist yet" do
-          before do
-            allow(File).to receive(:exist?).with(pid_file_path).and_return(false)
-          end
-
-          it "archives the repo" do
-            expect(project.repository).to receive(:archive_repo)
-
-            subject.perform(project.id, "master", "zip")
-          end
-        end
-
-        context "when the archive pid file already exists" do
-          it "doesn't archive the repo" do
-            expect(project.repository).not_to receive(:archive_repo)
-
-            subject.perform(project.id, "master", "zip")
-          end
-        end
-      end
-    end
-  end
-end
diff --git a/spec/workers/stuck_ci_builds_worker_spec.rb b/spec/workers/stuck_ci_builds_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f9d87d97014f94e9e791882bbc7dfe6b5a875f66
--- /dev/null
+++ b/spec/workers/stuck_ci_builds_worker_spec.rb
@@ -0,0 +1,44 @@
+require "spec_helper"
+
+describe StuckCiBuildsWorker do
+  let!(:build) { create :ci_build }
+
+  subject do
+    build.reload
+    build.status
+  end
+
+  %w(pending running).each do |status|
+    context "#{status} build" do
+      before do
+        build.update!(status: status)
+      end
+
+      it 'gets dropped if it was updated over 2 days ago' do
+        build.update!(updated_at: 2.day.ago)
+        StuckCiBuildsWorker.new.perform
+        is_expected.to eq('failed')
+      end
+
+      it "is still #{status}" do
+        build.update!(updated_at: 1.minute.ago)
+        StuckCiBuildsWorker.new.perform
+        is_expected.to eq(status)
+      end
+    end
+  end
+
+  %w(success failed canceled).each do |status|
+    context "#{status} build" do
+      before do
+        build.update!(status: status)
+      end
+
+      it "is still #{status}" do
+        build.update!(updated_at: 2.day.ago)
+        StuckCiBuildsWorker.new.perform
+        is_expected.to eq(status)
+      end
+    end
+  end
+end
diff --git a/vendor/assets/javascripts/clipboard.js b/vendor/assets/javascripts/clipboard.js
new file mode 100644
index 0000000000000000000000000000000000000000..1b1f4f0bd63e9f38ea635607ab0aa9971e5c650d
--- /dev/null
+++ b/vendor/assets/javascripts/clipboard.js
@@ -0,0 +1,621 @@
+/*!
+ * clipboard.js v1.4.2
+ * https://zenorocha.github.io/clipboard.js
+ *
+ * Licensed MIT © Zeno Rocha
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+/**
+ * Module dependencies.
+ */
+
+var closest = require('closest')
+  , event = require('component-event');
+
+/**
+ * Delegate event `type` to `selector`
+ * and invoke `fn(e)`. A callback function
+ * is returned which may be passed to `.unbind()`.
+ *
+ * @param {Element} el
+ * @param {String} selector
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @return {Function}
+ * @api public
+ */
+
+// Some events don't bubble, so we want to bind to the capture phase instead
+// when delegating.
+var forceCaptureEvents = ['focus', 'blur'];
+
+exports.bind = function(el, selector, type, fn, capture){
+  if (forceCaptureEvents.indexOf(type) !== -1) capture = true;
+
+  return event.bind(el, type, function(e){
+    var target = e.target || e.srcElement;
+    e.delegateTarget = closest(target, selector, true, el);
+    if (e.delegateTarget) fn.call(el, e);
+  }, capture);
+};
+
+/**
+ * Unbind event `type`'s callback `fn`.
+ *
+ * @param {Element} el
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @api public
+ */
+
+exports.unbind = function(el, type, fn, capture){
+  if (forceCaptureEvents.indexOf(type) !== -1) capture = true;
+
+  event.unbind(el, type, fn, capture);
+};
+
+},{"closest":2,"component-event":4}],2:[function(require,module,exports){
+var matches = require('matches-selector')
+
+module.exports = function (element, selector, checkYoSelf) {
+  var parent = checkYoSelf ? element : element.parentNode
+
+  while (parent && parent !== document) {
+    if (matches(parent, selector)) return parent;
+    parent = parent.parentNode
+  }
+}
+
+},{"matches-selector":3}],3:[function(require,module,exports){
+
+/**
+ * Element prototype.
+ */
+
+var proto = Element.prototype;
+
+/**
+ * Vendor function.
+ */
+
+var vendor = proto.matchesSelector
+  || proto.webkitMatchesSelector
+  || proto.mozMatchesSelector
+  || proto.msMatchesSelector
+  || proto.oMatchesSelector;
+
+/**
+ * Expose `match()`.
+ */
+
+module.exports = match;
+
+/**
+ * Match `el` to `selector`.
+ *
+ * @param {Element} el
+ * @param {String} selector
+ * @return {Boolean}
+ * @api public
+ */
+
+function match(el, selector) {
+  if (vendor) return vendor.call(el, selector);
+  var nodes = el.parentNode.querySelectorAll(selector);
+  for (var i = 0; i < nodes.length; ++i) {
+    if (nodes[i] == el) return true;
+  }
+  return false;
+}
+},{}],4:[function(require,module,exports){
+var bind = window.addEventListener ? 'addEventListener' : 'attachEvent',
+    unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
+    prefix = bind !== 'addEventListener' ? 'on' : '';
+
+/**
+ * Bind `el` event `type` to `fn`.
+ *
+ * @param {Element} el
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @return {Function}
+ * @api public
+ */
+
+exports.bind = function(el, type, fn, capture){
+  el[bind](prefix + type, fn, capture || false);
+  return fn;
+};
+
+/**
+ * Unbind `el` event `type`'s callback `fn`.
+ *
+ * @param {Element} el
+ * @param {String} type
+ * @param {Function} fn
+ * @param {Boolean} capture
+ * @return {Function}
+ * @api public
+ */
+
+exports.unbind = function(el, type, fn, capture){
+  el[unbind](prefix + type, fn, capture || false);
+  return fn;
+};
+},{}],5:[function(require,module,exports){
+function E () {
+	// Keep this empty so it's easier to inherit from
+  // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
+}
+
+E.prototype = {
+	on: function (name, callback, ctx) {
+    var e = this.e || (this.e = {});
+    
+    (e[name] || (e[name] = [])).push({
+      fn: callback,
+      ctx: ctx
+    });
+    
+    return this;
+  },
+
+  once: function (name, callback, ctx) {
+    var self = this;
+    var fn = function () {
+      self.off(name, fn);
+      callback.apply(ctx, arguments);
+    };
+    
+    return this.on(name, fn, ctx);
+  },
+
+  emit: function (name) {
+    var data = [].slice.call(arguments, 1);
+    var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
+    var i = 0;
+    var len = evtArr.length;
+    
+    for (i; i < len; i++) {
+      evtArr[i].fn.apply(evtArr[i].ctx, data);
+    }
+    
+    return this;
+  },
+
+  off: function (name, callback) {
+    var e = this.e || (this.e = {});
+    var evts = e[name];
+    var liveEvents = [];
+    
+    if (evts && callback) {
+      for (var i = 0, len = evts.length; i < len; i++) {
+        if (evts[i].fn !== callback) liveEvents.push(evts[i]);
+      }
+    }
+    
+    // Remove event from queue to prevent memory leak
+    // Suggested by https://github.com/lazd
+    // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
+
+    (liveEvents.length) 
+      ? e[name] = liveEvents
+      : delete e[name];
+    
+    return this;
+  }
+};
+
+module.exports = E;
+
+},{}],6:[function(require,module,exports){
+/**
+ * Inner class which performs selection from either `text` or `target`
+ * properties and then executes copy or cut operations.
+ */
+'use strict';
+
+exports.__esModule = true;
+
+var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+var ClipboardAction = (function () {
+    /**
+     * @param {Object} options
+     */
+
+    function ClipboardAction(options) {
+        _classCallCheck(this, ClipboardAction);
+
+        this.resolveOptions(options);
+        this.initSelection();
+    }
+
+    /**
+     * Defines base properties passed from constructor.
+     * @param {Object} options
+     */
+
+    ClipboardAction.prototype.resolveOptions = function resolveOptions() {
+        var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+
+        this.action = options.action;
+        this.emitter = options.emitter;
+        this.target = options.target;
+        this.text = options.text;
+        this.trigger = options.trigger;
+
+        this.selectedText = '';
+    };
+
+    /**
+     * Decides which selection strategy is going to be applied based
+     * on the existence of `text` and `target` properties.
+     */
+
+    ClipboardAction.prototype.initSelection = function initSelection() {
+        if (this.text && this.target) {
+            throw new Error('Multiple attributes declared, use either "target" or "text"');
+        } else if (this.text) {
+            this.selectFake();
+        } else if (this.target) {
+            this.selectTarget();
+        } else {
+            throw new Error('Missing required attributes, use either "target" or "text"');
+        }
+    };
+
+    /**
+     * Creates a fake textarea element, sets its value from `text` property,
+     * and makes a selection on it.
+     */
+
+    ClipboardAction.prototype.selectFake = function selectFake() {
+        var _this = this;
+
+        this.removeFake();
+
+        this.fakeHandler = document.body.addEventListener('click', function () {
+            return _this.removeFake();
+        });
+
+        this.fakeElem = document.createElement('textarea');
+        this.fakeElem.style.position = 'absolute';
+        this.fakeElem.style.left = '-9999px';
+        this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
+        this.fakeElem.setAttribute('readonly', '');
+        this.fakeElem.value = this.text;
+        this.selectedText = this.text;
+
+        document.body.appendChild(this.fakeElem);
+
+        this.fakeElem.select();
+        this.copyText();
+    };
+
+    /**
+     * Only removes the fake element after another click event, that way
+     * a user can hit `Ctrl+C` to copy because selection still exists.
+     */
+
+    ClipboardAction.prototype.removeFake = function removeFake() {
+        if (this.fakeHandler) {
+            document.body.removeEventListener('click');
+            this.fakeHandler = null;
+        }
+
+        if (this.fakeElem) {
+            document.body.removeChild(this.fakeElem);
+            this.fakeElem = null;
+        }
+    };
+
+    /**
+     * Selects the content from element passed on `target` property.
+     */
+
+    ClipboardAction.prototype.selectTarget = function selectTarget() {
+        if (this.target.nodeName === 'INPUT' || this.target.nodeName === 'TEXTAREA') {
+            this.target.select();
+            this.selectedText = this.target.value;
+        } else {
+            var range = document.createRange();
+            var selection = window.getSelection();
+
+            selection.removeAllRanges();
+            range.selectNodeContents(this.target);
+            selection.addRange(range);
+            this.selectedText = selection.toString();
+        }
+
+        this.copyText();
+    };
+
+    /**
+     * Executes the copy operation based on the current selection.
+     */
+
+    ClipboardAction.prototype.copyText = function copyText() {
+        var succeeded = undefined;
+
+        try {
+            succeeded = document.execCommand(this.action);
+        } catch (err) {
+            succeeded = false;
+        }
+
+        this.handleResult(succeeded);
+    };
+
+    /**
+     * Fires an event based on the copy operation result.
+     * @param {Boolean} succeeded
+     */
+
+    ClipboardAction.prototype.handleResult = function handleResult(succeeded) {
+        if (succeeded) {
+            this.emitter.emit('success', {
+                action: this.action,
+                text: this.selectedText,
+                trigger: this.trigger,
+                clearSelection: this.clearSelection.bind(this)
+            });
+        } else {
+            this.emitter.emit('error', {
+                action: this.action,
+                trigger: this.trigger,
+                clearSelection: this.clearSelection.bind(this)
+            });
+        }
+    };
+
+    /**
+     * Removes current selection and focus from `target` element.
+     */
+
+    ClipboardAction.prototype.clearSelection = function clearSelection() {
+        if (this.target) {
+            this.target.blur();
+        }
+
+        window.getSelection().removeAllRanges();
+    };
+
+    /**
+     * Sets the `action` to be performed which can be either 'copy' or 'cut'.
+     * @param {String} action
+     */
+
+    /**
+     * Destroy lifecycle.
+     */
+
+    ClipboardAction.prototype.destroy = function destroy() {
+        this.removeFake();
+    };
+
+    _createClass(ClipboardAction, [{
+        key: 'action',
+        set: function set() {
+            var action = arguments.length <= 0 || arguments[0] === undefined ? 'copy' : arguments[0];
+
+            this._action = action;
+
+            if (this._action !== 'copy' && this._action !== 'cut') {
+                throw new Error('Invalid "action" value, use either "copy" or "cut"');
+            }
+        },
+
+        /**
+         * Gets the `action` property.
+         * @return {String}
+         */
+        get: function get() {
+            return this._action;
+        }
+
+        /**
+         * Sets the `target` property using an element
+         * that will be have its content copied.
+         * @param {Element} target
+         */
+    }, {
+        key: 'target',
+        set: function set(target) {
+            if (target !== undefined) {
+                if (target && typeof target === 'object' && target.nodeType === 1) {
+                    this._target = target;
+                } else {
+                    throw new Error('Invalid "target" value, use a valid Element');
+                }
+            }
+        },
+
+        /**
+         * Gets the `target` property.
+         * @return {String|HTMLElement}
+         */
+        get: function get() {
+            return this._target;
+        }
+    }]);
+
+    return ClipboardAction;
+})();
+
+exports['default'] = ClipboardAction;
+module.exports = exports['default'];
+
+},{}],7:[function(require,module,exports){
+'use strict';
+
+exports.__esModule = true;
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+
+var _clipboardAction = require('./clipboard-action');
+
+var _clipboardAction2 = _interopRequireDefault(_clipboardAction);
+
+var _delegateEvents = require('delegate-events');
+
+var _delegateEvents2 = _interopRequireDefault(_delegateEvents);
+
+var _tinyEmitter = require('tiny-emitter');
+
+var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter);
+
+/**
+ * Base class which takes a selector, delegates a click event to it,
+ * and instantiates a new `ClipboardAction` on each click.
+ */
+
+var Clipboard = (function (_Emitter) {
+    _inherits(Clipboard, _Emitter);
+
+    /**
+     * @param {String} selector
+     * @param {Object} options
+     */
+
+    function Clipboard(selector, options) {
+        _classCallCheck(this, Clipboard);
+
+        _Emitter.call(this);
+
+        this.resolveOptions(options);
+        this.delegateClick(selector);
+    }
+
+    /**
+     * Helper function to retrieve attribute value.
+     * @param {String} suffix
+     * @param {Element} element
+     */
+
+    /**
+     * Defines if attributes would be resolved using internal setter functions
+     * or custom functions that were passed in the constructor.
+     * @param {Object} options
+     */
+
+    Clipboard.prototype.resolveOptions = function resolveOptions() {
+        var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+
+        this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
+        this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
+        this.text = typeof options.text === 'function' ? options.text : this.defaultText;
+    };
+
+    /**
+     * Delegates a click event on the passed selector.
+     * @param {String} selector
+     */
+
+    Clipboard.prototype.delegateClick = function delegateClick(selector) {
+        var _this = this;
+
+        this.binding = _delegateEvents2['default'].bind(document.body, selector, 'click', function (e) {
+            return _this.onClick(e);
+        });
+    };
+
+    /**
+     * Undelegates a click event on body.
+     * @param {String} selector
+     */
+
+    Clipboard.prototype.undelegateClick = function undelegateClick() {
+        _delegateEvents2['default'].unbind(document.body, 'click', this.binding);
+    };
+
+    /**
+     * Defines a new `ClipboardAction` on each click event.
+     * @param {Event} e
+     */
+
+    Clipboard.prototype.onClick = function onClick(e) {
+        if (this.clipboardAction) {
+            this.clipboardAction = null;
+        }
+
+        this.clipboardAction = new _clipboardAction2['default']({
+            action: this.action(e.delegateTarget),
+            target: this.target(e.delegateTarget),
+            text: this.text(e.delegateTarget),
+            trigger: e.delegateTarget,
+            emitter: this
+        });
+    };
+
+    /**
+     * Default `action` lookup function.
+     * @param {Element} trigger
+     */
+
+    Clipboard.prototype.defaultAction = function defaultAction(trigger) {
+        return getAttributeValue('action', trigger);
+    };
+
+    /**
+     * Default `target` lookup function.
+     * @param {Element} trigger
+     */
+
+    Clipboard.prototype.defaultTarget = function defaultTarget(trigger) {
+        var selector = getAttributeValue('target', trigger);
+
+        if (selector) {
+            return document.querySelector(selector);
+        }
+    };
+
+    /**
+     * Default `text` lookup function.
+     * @param {Element} trigger
+     */
+
+    Clipboard.prototype.defaultText = function defaultText(trigger) {
+        return getAttributeValue('text', trigger);
+    };
+
+    /**
+     * Destroy lifecycle.
+     */
+
+    Clipboard.prototype.destroy = function destroy() {
+        this.undelegateClick();
+
+        if (this.clipboardAction) {
+            this.clipboardAction.destroy();
+            this.clipboardAction = null;
+        }
+    };
+
+    return Clipboard;
+})(_tinyEmitter2['default']);
+
+function getAttributeValue(suffix, element) {
+    var attribute = 'data-clipboard-' + suffix;
+
+    if (!element.hasAttribute(attribute)) {
+        return;
+    }
+
+    return element.getAttribute(attribute);
+}
+
+exports['default'] = Clipboard;
+module.exports = exports['default'];
+
+},{"./clipboard-action":6,"delegate-events":1,"tiny-emitter":5}]},{},[7])(7)
+});
\ No newline at end of file