diff --git a/.flayignore b/.flayignore
new file mode 100644
index 0000000000000000000000000000000000000000..9c9875d4f9ef387c191ce57d128a7c2af1abd8b3
--- /dev/null
+++ b/.flayignore
@@ -0,0 +1 @@
+*.erb
diff --git a/.gitignore b/.gitignore
index 73bde4cc7615d532ec8c3fc0b6f5ce1a546ba553..f5b6427ca033d60c0b69d5ce1d1c9c81eda22b2b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,8 +37,10 @@ nohup.out
 public/assets/
 public/uploads.*
 public/uploads/
+shared/artifacts/
 rails_best_practices_output.html
 /tags
 tmp/
 vendor/bundle/*
 builds/*
+shared/*
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cf6d28b01af06af94652b4ee11f42d007fcc7975..141e7ba41dec9c7c570747065d23aa8fbaed432f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -73,3 +73,18 @@ brakeman:
   tags:
     - ruby
     - mysql
+
+flog:
+  script:
+    - bundle exec rake flog
+  tags:
+    - ruby
+    - mysql
+
+flay:
+  script:
+    - bundle exec rake flay
+  tags:
+    - ruby
+    - mysql
+  allow_failure: true
diff --git a/CHANGELOG b/CHANGELOG
index aba823948a73d4377b719d3684702a21a66ef18f..3c22df7c9a361d9382d0865d9d6ea3e20f5ef77a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,8 +1,92 @@
 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)
+  - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (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
+  - Bump GitLab-Workhorse to 0.4.1
+  - Allow to define cache in `.gitlab-ci.yml`
+  - 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
+  - Fix omniauth documentation setting for omnibus configuration (Jon Cairns)
+  - 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
+  - Remove deprecated dumped yaml file generated from previous job definitions
+  - Fix incoming email config defaults
+  - Show specific runners from projects where user is master or owner
+  - MR target branch is now visible on a list view when it is different from project's default one
+  - Improve Continuous Integration graphs page
+  - Make color of "Accept Merge Request" button consistent with current build status
+  - Add ignore white space option in merge request diff and commit and compare view
+  - Ability to add release notes (markdown text and attachments) to git tags (aka Releases)
+  - Relative links from a repositories README.md now link to the default branch
+  - Fix trailing whitespace issue in merge request/issue title
+  - Fix bug when milestone/label filter was empty for dashboard issues page
+  - Add ability to create milestone in group projects from single form
+
+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
+  - 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)
@@ -10,8 +94,10 @@ v 8.1.0 (unreleased)
   - 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)
@@ -22,11 +108,13 @@ v 8.1.0 (unreleased)
   - 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)
+  - Added build artifacts
   - Fix grammar in admin area "labels" .nothing-here-block when no labels exist.
   - Move CI runners page to project settings area
   - Move CI variables page to project settings area
@@ -45,7 +133,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
@@ -53,6 +141,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
@@ -61,6 +151,14 @@ v 8.1.0 (unreleased)
   - 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..5d85c9f3fcae5e4d97f69d92e99d2d316148dfae 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,7 +10,7 @@ By submitting code as an individual you agree to the [individual contributor lic
 
 ## Security vulnerability disclosure
 
-Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
+Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
 
 ## Closing policy for issues and merge requests
 
@@ -35,7 +35,7 @@ The [GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab
 
 Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose. Please keep feature requests as small and simple as possible, complex ones might be edited to make them small and simple.
 
-Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
+Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](https://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
 
 ### Issue tracker guidelines
 
@@ -72,7 +72,7 @@ If you can, please submit a merge request with the fix or improvements including
 1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
 1. Add your changes to the [CHANGELOG](CHANGELOG)
 1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message
-1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
+1. If you have multiple commits please combine them into one commit by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
 1. Push the commit to your fork
 1. Submit a merge request (MR) to the master branch
 1. The MR title should describe the change you want to make
@@ -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.
@@ -180,4 +181,4 @@ This code of conduct applies both within project spaces and in public spaces whe
 
 Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com
 
-This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
+This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
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 b/GITLAB_WORKHORSE
new file mode 100644
index 0000000000000000000000000000000000000000..267577d47e497a0630bc454b3f74c4fd9a10ced4
--- /dev/null
+++ b/GITLAB_WORKHORSE
@@ -0,0 +1 @@
+0.4.1
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 4fbd37f6c8bd4b06503c3e4d0f5ed6959e231ef7..8a19885bcb199b49e5cfb081e1b43476d924e50a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -19,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'
@@ -39,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.19'
+gem "gitlab_git", '~> 7.2.20'
 
 # LDAP Auth
 # GitLab fork with several improvements to original library. For full list of changes
@@ -50,20 +51,16 @@ 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'
+gem 'grape',        '~> 0.13.0'
 gem 'grape-entity', '~> 0.4.2'
 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'
@@ -94,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'
@@ -112,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'
 
@@ -184,7 +181,7 @@ 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'
@@ -197,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'
@@ -214,11 +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 'active_record_query_trace', require: false
-  gem 'rack-lineprof', platform: :mri
+  gem 'rblineprof', platform: :mri, require: false
 
   # Better errors handler
   gem 'better_errors', '~> 1.0.1'
@@ -264,6 +259,8 @@ group :development, :test do
   gem 'rubocop',  '~> 0.28.0',  require: false
   gem 'coveralls',  '~> 0.8.2', require: false
   gem 'simplecov', '~> 0.10.0', require: false
+  gem 'flog', require: false
+  gem 'flay', require: false
 
   gem 'benchmark-ips', require: false
 end
diff --git a/Gemfile.lock b/Gemfile.lock
index b92d9766d3a808c8abf840d21cf694ed144c22da..99cdc2a50aee35acf5dbc4b2dbc35bf62cc220a1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -17,7 +17,6 @@ GEM
       activesupport (= 4.1.12)
       builder (~> 3.1)
       erubis (~> 2.7.0)
-    active_record_query_trace (1.5)
     activemodel (4.1.12)
       activesupport (= 4.1.12)
       builder (~> 3.1)
@@ -108,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)
@@ -176,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)
@@ -195,6 +194,12 @@ GEM
     ffi (1.9.10)
     fission (0.5.0)
       CFPropertyList (~> 2.2)
+    flay (2.6.1)
+      ruby_parser (~> 3.0)
+      sexp_processor (~> 4.0)
+    flog (4.3.2)
+      ruby_parser (~> 3.1, > 3.1.0)
+      sexp_processor (~> 4.4)
     flowdock (0.7.0)
       httparty (~> 0.7)
       multi_json
@@ -267,6 +272,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)
@@ -277,17 +287,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.19)
+    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)
@@ -306,10 +312,10 @@ GEM
     gon (5.0.4)
       actionpack (>= 2.3.0)
       json
-    grape (0.6.1)
+    grape (0.13.0)
       activesupport
       builder
-      hashie (>= 1.2.0)
+      hashie (>= 2.1.0)
       multi_json (>= 1.3.2)
       multi_xml (>= 0.5.2)
       rack (>= 1.3.0)
@@ -354,7 +360,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)
@@ -406,7 +412,7 @@ GEM
     newrelic_rpm (3.9.4.245)
     nokogiri (1.6.6.2)
       mini_portile (~> 0.6.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)
@@ -423,6 +429,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)
@@ -489,12 +497,6 @@ GEM
     rack-attack (4.3.0)
       rack
     rack-cors (0.4.0)
-    rack-lineprof (0.0.3)
-      rack (~> 1.5)
-      rblineprof (~> 0.3.6)
-      term-ansicolor (~> 1.3)
-    rack-mini-profiler (0.9.7)
-      rack (>= 1.1.3)
     rack-mount (0.8.3)
       rack (>= 1.0.0)
     rack-oauth2 (1.0.10)
@@ -539,7 +541,7 @@ GEM
       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)
@@ -615,7 +617,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)
@@ -689,8 +691,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)
@@ -777,7 +785,6 @@ PLATFORMS
 DEPENDENCIES
   RedCloth (~> 4.2.9)
   ace-rails-ap (~> 2.0.1)
-  active_record_query_trace
   activerecord-deprecated_finders (~> 1.0.3)
   activerecord-session_store (~> 0.1.0)
   acts-as-taggable-on (~> 3.4)
@@ -800,7 +807,7 @@ DEPENDENCIES
   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)
@@ -820,27 +827,29 @@ DEPENDENCIES
   enumerize (~> 0.7.0)
   factory_girl_rails (~> 4.3.0)
   ffaker (~> 2.0.0)
+  flay
+  flog
   fog (~> 1.25.0)
   font-awesome-rails (~> 4.2)
   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.19)
+  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 (~> 0.13.0)
   grape-entity (~> 0.4.2)
   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)
@@ -854,11 +863,12 @@ DEPENDENCIES
   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)
@@ -875,13 +885,12 @@ DEPENDENCIES
   quiet_assets (~> 1.0.2)
   rack-attack (~> 4.3.0)
   rack-cors (~> 0.4.0)
-  rack-lineprof
-  rack-mini-profiler (~> 0.9.0)
   rack-oauth2 (~> 1.0.5)
   rails (= 4.1.12)
   raphael-rails (~> 2.1.2)
+  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)
@@ -909,8 +918,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)
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 a6f65642d78df4f1d8d8e39bb546aad6de570984..bf4cd85aee012187e51e1b8e548eda9cc1cfbe9a 100644
--- a/app/assets/javascripts/blob/edit_blob.js.coffee
+++ b/app/assets/javascripts/blob/edit_blob.js.coffee
@@ -18,10 +18,10 @@ class @EditBlob
         $('.form-group.destination').hide()
         $('#create_merge_request').prop('checked', false)
 
-    $(".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 f4e06ad088bc1573c9383bd71fdd31c547e189db..c2d6c7acfefc4819c3f5e21ca4dcd72322502686 100644
--- a/app/assets/javascripts/blob/new_blob.js.coffee
+++ b/app/assets/javascripts/blob/new_blob.js.coffee
@@ -18,10 +18,10 @@ class @NewBlob
         $('.form-group.destination').hide()
         $('#create_merge_request').prop('checked', false)
 
-    $(".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..4059fc39c67b5d20c2c58e7cfb59b4048f8b836e 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -28,6 +28,8 @@ class Dispatcher
       when 'projects:milestones:new', 'projects:milestones:edit'
         new ZenMode()
         new DropzoneInput($('.milestone-form'))
+      when 'groups:milestones:new'
+        new ZenMode()
       when 'projects:compare:show'
         new Diff()
       when 'projects:issues:new','projects:issues:edit'
@@ -39,6 +41,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/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/header.scss b/app/assets/stylesheets/framework/header.scss
index 91e6975e2694eafd7a5a01a3a115199fd3184d1d..02ea91602e8ee3f360fd8fd2ea04968b417a716f 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -118,6 +118,10 @@ header {
       }
     }
   }
+
+  .impersonation i {
+    color: $red-normal;
+  }
 }
 
 @mixin collapsed-header {
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 6ccc084526ad4e9358d97d6ce02c4317450110b6..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;
   }
 
@@ -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/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 6bc9b1c3eaf303741b1c0701f0d345ad74328e76..c0defa82bff993df7292e56e93e4f3441a223316 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_controller.rb b/app/controllers/admin/application_controller.rb
index 56e24386463db3a95e794faf0ed9ed83df635336..9083bfb41cfdb494dcba62e0bb6ec25186dbdfbd 100644
--- a/app/controllers/admin/application_controller.rb
+++ b/app/controllers/admin/application_controller.rb
@@ -8,4 +8,10 @@ class Admin::ApplicationController < ApplicationController
   def authenticate_admin!
     return render_404 unless current_user.is_admin?
   end
+
+  def authorize_impersonator!
+    if session[:impersonator_id]
+      User.find_by!(username: session[:impersonator_id]).admin?
+    end
+  end
 end
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 7c134d2ec9b7f30c28bf957e9d9f235d0ebefde2..a9bcfc7456ad394b64ec2c10939b38de44a0c67e 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -55,7 +55,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
       :default_snippet_visibility,
       :restricted_signup_domains_raw,
       :version_check_enabled,
+      :admin_notification_email,
       :user_oauth_applications,
+      :shared_runners_enabled,
+      :max_artifacts_size,
       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/impersonation_controller.rb b/app/controllers/admin/impersonation_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0382402afa6432de55f9c14e4b0a13dd825f5e88
--- /dev/null
+++ b/app/controllers/admin/impersonation_controller.rb
@@ -0,0 +1,32 @@
+class Admin::ImpersonationController < Admin::ApplicationController
+  skip_before_action :authenticate_admin!, only: :destroy
+
+  before_action :user
+  before_action :authorize_impersonator!
+
+  def create
+    session[:impersonator_id] = current_user.username
+    session[:impersonator_return_to] = request.env['HTTP_REFERER']
+
+    warden.set_user(user, scope: 'user')
+
+    flash[:alert] = "You are impersonating #{user.username}."
+
+    redirect_to root_path
+  end
+
+  def destroy
+    redirect = session[:impersonator_return_to]
+
+    warden.set_user(user, scope: 'user')
+
+    session[:impersonator_return_to] = nil
+    session[:impersonator_id] = nil
+
+    redirect_to redirect || root_path
+  end
+
+  def user
+    @user ||= User.find_by!(username: params[:id] || session[:impersonator_id])
+  end
+end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 00f41a10dd11d093fa78e3b3e31b154cf28d7158..d7c927d444cbc4b43d0d5bfe20f63bb8e2c5a5c8 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -33,42 +33,36 @@ 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
 
-  def login_as
-    sign_in(user)
-    flash[:alert] = "Logged in as #{user.username}"
-    redirect_to root_path
-  end
-
   def disable_two_factor
     user.disable_two_factor!
     redirect_to admin_user_path(user),
@@ -138,7 +132,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 +151,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 258e37e98c098281d3b538ee1110312edef2ac9f..6f87ee08b2d38115bce4a59780edcb08d4be1469 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -34,6 +34,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
@@ -56,13 +60,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)
@@ -121,7 +120,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
@@ -373,4 +371,17 @@ class ApplicationController < ActionController::Base
       }
     }
   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 110954a612d04cfee9bab683345fb1e772b47596..0cafad27418b043913aba0a843d9b888d07ada12 100644
--- a/app/controllers/ci/admin/runners_controller.rb
+++ b/app/controllers/ci/admin/runners_controller.rb
@@ -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
 
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..8406399fb60a3cb98d7d1279b3f9feb19af31512 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
@@ -21,10 +26,6 @@ module Ci
       redirect_to namespace_project_runners_path(project.gl_project.namespace, project.gl_project)
     end
 
-    def dumped_yaml
-      send_data @project.generated_yaml_config, filename: '.gitlab-ci.yml'
-    end
-
     protected
 
     def project
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/concerns/global_milestones.rb b/app/controllers/concerns/global_milestones.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b428249acd3eed58de5dbdad1fb8a033ac689d81
--- /dev/null
+++ b/app/controllers/concerns/global_milestones.rb
@@ -0,0 +1,19 @@
+module GlobalMilestones
+  extend ActiveSupport::Concern
+
+  def milestones
+    @milestones = MilestonesFinder.new.execute(@projects, params)
+    @milestones = GlobalMilestone.build_collection(@milestones)
+    @milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE)
+  end
+
+  def milestone
+    milestones = Milestone.of_projects(@projects).where(title: params[:title])
+
+    if milestones.present?
+      @milestone = GlobalMilestone.new(params[:title], milestones)
+    else
+      render_404
+    end
+  end
+end
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 53896d4f2c73a356334d9e88b36f96b22c3aa71b..2bdce0f8a0025326c4e6742931fb262b1878cc63 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -1,34 +1,19 @@
 class Dashboard::MilestonesController < Dashboard::ApplicationController
-  before_action :load_projects
+  include GlobalMilestones
+
+  before_action :projects
+  before_action :milestones, only: [:index]
+  before_action :milestone, only: [:show]
 
   def index
-    project_milestones = case params[:state]
-                         when 'all'; state
-                         when 'closed'; state('closed')
-                         else state('active')
-                         end
-    @dashboard_milestones = Milestones::GroupService.new(project_milestones).execute
-    @dashboard_milestones = Kaminari.paginate_array(@dashboard_milestones).page(params[:page]).per(PER_PAGE)
   end
 
   def show
-    project_milestones = Milestone.where(project_id: @projects).order("due_date ASC")
-    @dashboard_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
   end
 
   private
 
-  def load_projects
-    @projects = current_user.authorized_projects.sorted_by_activity.non_archived
-  end
-
-  def title
-    params[:title]
-  end
-
-  def state(state = nil)
-    conditions = { project_id: @projects }
-    conditions.reverse_merge!(state: state) if state
-    Milestone.where(conditions).order("title ASC")
+  def projects
+    @projects ||= current_user.authorized_projects.sorted_by_activity.non_archived
   end
 end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 4ebb3d7276ed13b9ff800209bed569e8617369c9..b2c1fa4230c9f01ce09e94098e8e5567d55dfdf4 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -1,5 +1,6 @@
 class DashboardController < Dashboard::ApplicationController
   before_action :event_filter, only: :activity
+  before_action :projects, only: [:issues, :merge_requests]
 
   respond_to :html
 
@@ -47,4 +48,8 @@ class DashboardController < Dashboard::ApplicationController
     @events = @event_filter.apply_filter(@events).with_associations
     @events = @events.limit(20).offset(params[:offset] || 0)
   end
+
+  def projects
+    @projects ||= current_user.authorized_projects.sorted_by_activity.non_archived
+  end
 end
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index 6878d4bc07ecbc218f2c99529189888f52a7e4fd..be801858eafed5db195b01c1ede0a3bf83175282 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -1,8 +1,13 @@
 class Groups::ApplicationController < ApplicationController
   layout 'group'
+  before_action :group
 
   private
-  
+
+  def group
+    @group ||= Group.find_by(path: params[:group_id])
+  end
+
   def authorize_read_group!
     unless @group and can?(current_user, :read_group, @group)
       if current_user.nil?
@@ -12,13 +17,13 @@ class Groups::ApplicationController < ApplicationController
       end
     end
   end
-  
+
   def authorize_admin_group!
     unless can?(current_user, :admin_group, group)
       return render_404
     end
   end
-  
+
   def authorize_admin_group_member!
     unless can?(current_user, :admin_group_member, group)
       return render_403
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index 6aa64222f7789601cfbfbca55ed02ee5b69d8cb1..76c87366baad198859a3dd9dfd77849c45437fb2 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -1,8 +1,6 @@
-class Groups::AvatarsController < ApplicationController
+class Groups::AvatarsController < Groups::ApplicationController
   def destroy
-    @group = Group.find_by(path: params[:group_id])
     @group.remove_avatar!
-
     @group.save
 
     redirect_to edit_group_path(@group)
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 91518c44a988e900a00ccdf6e5cd93398429dab7..b25957a06e2a82c192c11e5aa45343dbb2e28531 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -1,6 +1,5 @@
 class Groups::GroupMembersController < Groups::ApplicationController
   skip_before_action :authenticate_user!, only: [:index]
-  before_action :group
 
   # Authorize
   before_action :authorize_read_group!
@@ -80,10 +79,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
 
   protected
 
-  def group
-    @group ||= Group.find_by(path: params[:group_id])
-  end
-
   def member_params
     params.require(:group_member).permit(:access_level, :user_id)
   end
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 669f7f3126df188bb34056a3cd7246506e5f9b1a..10233222ee1b8f76c6a78fe1a29c05cfbf33122a 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -1,54 +1,55 @@
 class Groups::MilestonesController < Groups::ApplicationController
-  before_action :authorize_group_milestone!, only: :update
+  include GlobalMilestones
+
+  before_action :projects
+  before_action :milestones, only: [:index]
+  before_action :milestone, only: [:show, :update]
+  before_action :authorize_group_milestone!, only: [:create, :update]
 
   def index
-    project_milestones = case params[:state]
-                         when 'all'; state
-                         when 'closed'; state('closed')
-                         else state('active')
-                         end
-    @group_milestones = Milestones::GroupService.new(project_milestones).execute
-    @group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(PER_PAGE)
   end
 
-  def show
-    project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
-    @group_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
+  def new
+    @milestone = Milestone.new
   end
 
-  def update
-    project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
-    @group_milestones = Milestones::GroupService.new(project_milestones).milestone(title)
+  def create
+    project_ids = params[:milestone][:project_ids]
+    title = milestone_params[:title]
 
-    @group_milestones.milestones.each do |milestone|
-      Milestones::UpdateService.new(milestone.project, current_user, params[:milestone]).execute(milestone)
+    @group.projects.where(id: project_ids).each do |project|
+      Milestones::CreateService.new(project, current_user, milestone_params).execute
     end
 
-    respond_to do |format|
-      format.js
-      format.html do
-        redirect_to group_milestones_path(group)
-      end
+    redirect_to milestone_path(title)
+  end
+
+  def show
+  end
+
+  def update
+    @milestone.milestones.each do |milestone|
+      Milestones::UpdateService.new(milestone.project, current_user, milestone_params).execute(milestone)
     end
+
+    redirect_back_or_default(default: milestone_path(@milestone.title))
   end
 
   private
 
-  def group
-    @group ||= Group.find_by(path: params[:group_id])
+  def authorize_group_milestone!
+    return render_404 unless can?(current_user, :admin_milestones, group)
   end
 
-  def title
-    params[:title]
+  def milestone_params
+    params.require(:milestone).permit(:title, :description, :due_date, :state_event)
   end
 
-  def state(state = nil)
-    conditions = { project_id: group.projects }
-    conditions.reverse_merge!(state: state) if state
-    Milestone.where(conditions).order("title ASC")
+  def milestone_path(title)
+    group_milestone_path(@group, title.parameterize, title: title)
   end
 
-  def authorize_group_milestone!
-    return render_404 unless can?(current_user, :admin_group, group)
+  def projects
+    @projects ||= @group.projects
   end
 end
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/application_controller.rb b/app/controllers/projects/application_controller.rb
index 519d6d6127e44e596f1d2be05e5871e361eff3df..d3f926b62bcdaabdfaa9ddb37864c56aeba78a44 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -29,7 +29,7 @@ class Projects::ApplicationController < ApplicationController
   private
 
   def ci_enabled
-    return render_404 unless @project.gitlab_ci?
+    return render_404 unless @project.builds_enabled?
   end
 
   def ci_project
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index f49c094b591b8c004e530160047a020713bc93ce..d7fae64fcdd7e90c06eae95c09dde5eaab083122 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -175,7 +175,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 816012762ce9021c45687551792a77dc23457a21..4638f77b887e7440ea0b63a3e7629ff62b919d67 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -2,23 +2,25 @@ class Projects::BuildsController < Projects::ApplicationController
   before_action :ci_project
   before_action :build, except: [:index, :cancel_all]
 
-  before_action :authorize_admin_project!, except: [:index, :show, :status]
+  before_action :authorize_manage_builds!, except: [:index, :show, :status]
+  before_action :authorize_download_build_artifacts!, only: [:download]
 
   layout "project"
 
   def index
     @scope = params[:scope]
     @all_builds = project.ci_builds
+    @builds = @all_builds.order('created_at DESC')
     @builds =
       case @scope
       when 'all'
-        @all_builds
+        @builds
       when 'finished'
-        @all_builds.finished
+        @builds.finished
       else
-        @all_builds.running_or_pending
+        @builds.running_or_pending.reverse_order
       end
-    @builds = @builds.order('created_at DESC').page(params[:page]).per(30)
+    @builds = @builds.page(params[:page]).per(30)
   end
 
   def cancel_all
@@ -29,7 +31,7 @@ class Projects::BuildsController < Projects::ApplicationController
 
   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|
@@ -41,17 +43,25 @@ 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)
+    redirect_to build_path(build)
+  end
+
+  def download
+    unless artifacts_file.file_storage?
+      return redirect_to artifacts_file.url
+    end
+
+    unless artifacts_file.exists?
+      return not_found!
     end
+
+    send_file artifacts_file.path, disposition: 'attachment'
   end
 
   def status
@@ -70,7 +80,27 @@ class Projects::BuildsController < Projects::ApplicationController
     @build ||= ci_project.builds.unscoped.find_by!(id: params[:id])
   end
 
+  def artifacts_file
+    build.artifacts_file
+  end
+
   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
+
+  def authorize_download_build_artifacts!
+    unless can?(current_user, :download_build_artifacts, @project)
+      if current_user.nil?
+        return authenticate_user!
+      else
+        return render_404
+      end
+    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..55134e11d15907c673f04219f34049e694f472d4 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -12,14 +12,16 @@ class Projects::CompareController < Projects::ApplicationController
   def show
     base_ref = Addressable::URI.unescape(params[:from])
     @ref = head_ref = Addressable::URI.unescape(params[:to])
+    diff_options = { ignore_whitespace_change: true } if params[:w] == '1'
 
     compare_result = CompareService.new.
-      execute(@project, head_ref, @project, base_ref)
+      execute(@project, head_ref, @project, base_ref, diff_options)
 
     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 97485c101fb39433039a5d18266874c5b87f1d1e..e74c2905e4849dd3230c6729508cb5719435550b 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
@@ -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
@@ -151,10 +158,12 @@ class Projects::IssuesController < Projects::ApplicationController
   end
 
   def issue_params
-    params.require(:issue).permit(
+    permitted = params.require(:issue).permit(
       :title, :assignee_id, :position, :description,
       :milestone_id, :state_event, :task_num, label_ids: []
     )
+    params[:issue][:title].strip! if params[:issue][:title]
+    permitted
   end
 
   def bulk_update_params
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 98df6984bf7d7879ca5c4a9b0aff34dce4b42b82..188f0cc4cea632cfce7c96570c80475a0d8a356e 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
@@ -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
@@ -272,11 +276,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   end
 
   def merge_request_params
-    params.require(:merge_request).permit(
+    permitted = params.require(:merge_request).permit(
       :title, :assignee_id, :source_project_id, :source_branch,
       :target_project_id, :target_branch, :milestone_id,
       :state_event, :description, :task_num, label_ids: []
     )
+    params[:merge_request][:title].strip! if params[:merge_request][:title]
+    permitted
   end
 
   # Make sure merge requests created before 8.0
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/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/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index deb07a214160f396092597b6ae1127c2b4d0ed07..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
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 129068ef0192ed4c0e95d01c91237d212df75cd2..42dbb497e013ace446d1b38e76c37c9e84b35124 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -12,7 +12,7 @@ class Projects::ServicesController < Projects::ApplicationController
 
   # 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]
@@ -52,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
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..23453195e8521a3603beca537b2503432a6b8c19 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,14 @@ class ProjectsController < ApplicationController
     end
   end
 
+  def remove_fork
+    return access_denied! unless can?(current_user, :remove_fork_project, @project)
+
+    if @project.unlink_fork
+      flash[:notice] = 'The fork relationship has been removed.'
+    end
+  end
+
   def activity
     respond_to do |format|
       format.html
@@ -87,7 +100,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 +123,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 +148,7 @@ class ProjectsController < ApplicationController
 
   def archive
     return access_denied! unless can?(current_user, :archive_project, @project)
+
     @project.archive!
 
     respond_to do |format|
@@ -148,6 +158,7 @@ class ProjectsController < ApplicationController
 
   def unarchive
     return access_denied! unless can?(current_user, :archive_project, @project)
+
     @project.unarchive!
 
     respond_to do |format|
@@ -201,7 +212,8 @@ class ProjectsController < ApplicationController
     params.require(:project).permit(
       :name, :path, :description, :issues_tracker, :tag_list,
       :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
-      :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar
+      :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
+      :builds_enabled
     )
   end
 
@@ -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/finders/milestones_finder.rb b/app/finders/milestones_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b704e878903b5f460d0d8b3a33955e3f2d051829
--- /dev/null
+++ b/app/finders/milestones_finder.rb
@@ -0,0 +1,12 @@
+class MilestonesFinder
+  def execute(projects, params)
+    milestones = Milestone.of_projects(projects)
+    milestones = milestones.order("due_date ASC")
+
+    case params[:state]
+    when 'closed' then milestones.closed
+    when 'all' then milestones
+    else milestones.active
+    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/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/events_helper.rb b/app/helpers/events_helper.rb
index 6f69c2a9f329a4a19e84c5f9b14e388f312ba232..dde83ff36b50b2924682ce306b49c24055af5d58 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -108,19 +108,23 @@ module EventsHelper
         end
       end
     elsif event.push?
-      if event.push_with_commits? && event.md_ref?
-        if event.commits_count > 1
-          namespace_project_compare_url(event.project.namespace, event.project,
-                                        from: event.commit_from, to:
-                                        event.commit_to)
-        else
-          namespace_project_commit_url(event.project.namespace, event.project,
-                                       id: event.commit_to)
-        end
+      push_event_feed_url(event)
+    end
+  end
+
+  def push_event_feed_url(event)
+    if event.push_with_commits? && event.md_ref?
+      if event.commits_count > 1
+        namespace_project_compare_url(event.project.namespace, event.project,
+                                      from: event.commit_from, to:
+                                      event.commit_to)
       else
-        namespace_project_commits_url(event.project.namespace, event.project,
-                                      event.ref_name)
+        namespace_project_commit_url(event.project.namespace, event.project,
+                                     id: event.commit_to)
       end
+    else
+      namespace_project_commits_url(event.project.namespace, event.project,
+                                    event.ref_name)
     end
   end
 
@@ -198,7 +202,7 @@ module EventsHelper
         xml.link    href: event_link
         xml.title   truncate(event_title, length: 80)
         xml.updated event.created_at.xmlschema
-        xml.media   :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email)
+        xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email))
         xml.author do |author|
           xml.name event.author_name
           xml.email event.author_email
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/issues_helper.rb b/app/helpers/issues_helper.rb
index 6ddb37cd0dc9e7f4c343fd771210cb123b3cf9c6..beb083d82dc7640ec7d28175b1bd8f650d45ad95 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -74,7 +74,7 @@ module IssuesHelper
                                                     issue.project, issue)
       xml.title   truncate(issue.title, length: 80)
       xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
-      xml.media   :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email)
+      xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email))
       xml.author do |author|
         xml.name issue.author_name
         xml.email issue.author_email
@@ -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..795fb439f256ba5bba14acda80f938fbd61b91c7 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 = GlobalLabel.build_collection(labels)
+    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/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 37a5b58cce8be8efd3ad934662b239b529cc02d4..ad43892b6399cc14e1312afd52f34cbefa1654e9 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -28,7 +28,7 @@ module MilestonesHelper
         Milestone.where(project_id: @projects)
       end.active
 
-    grouped_milestones = Milestones::GroupService.new(milestones).execute
+    grouped_milestones = GlobalMilestone.build_collection(milestones)
     grouped_milestones.unshift(Milestone::None)
     grouped_milestones.unshift(Milestone::Any)
 
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index cf11f8e5320651c0afb767ddd287d6e11c59c673..499c655d2bf21991eb9955aff39ca948665cee64 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -16,40 +16,28 @@ module NotificationsHelper
   def notification_list_item(notification_level, user_membership)
     case notification_level
     when Notification::N_DISABLED
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_DISABLED)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do
-          icon('microphone-slash fw', text: 'Disabled')
-        end
-      end
+      update_notification_link(Notification::N_DISABLED, user_membership, 'Disabled', 'microphone-slash')
     when Notification::N_PARTICIPATING
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_PARTICIPATING)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do
-          icon('volume-up fw', text: 'Participate')
-        end
-      end
+      update_notification_link(Notification::N_PARTICIPATING, user_membership, 'Participate', 'volume-up')
     when Notification::N_WATCH
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_WATCH)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do
-          icon('eye fw', text: 'Watch')
-        end
-      end
+      update_notification_link(Notification::N_WATCH, user_membership, 'Watch', 'eye')
     when Notification::N_MENTION
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_MENTION)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION }  do
-          icon('at fw', text: 'On mention')
-        end
-      end
+      update_notification_link(Notification::N_MENTION, user_membership, 'On mention', 'at')
     when Notification::N_GLOBAL
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_GLOBAL)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_GLOBAL } do
-          icon('globe fw', text: 'Global')
-        end
-      end
+      update_notification_link(Notification::N_GLOBAL, user_membership, 'Global', 'globe')
     else
       # do nothing
     end
   end
 
+  def update_notification_link(notification_level, user_membership, title, icon)
+    content_tag(:li, class: active_level_for(user_membership, notification_level)) do
+      link_to '#', class: 'update-notification', data: { notification_level: notification_level } do
+        icon("#{icon} fw", text: title)
+      end
+    end
+  end
+
   def notification_label(user_membership)
     Notification.new(user_membership).to_s
   end
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 dbadbb745492d878bc2b4478079ab7c69adf3265..690ae2090db69c55e4223d1664b1bfbafbcb4544 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -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,7 +117,7 @@ module ProjectsHelper
       nav_tabs << :merge_requests
     end
 
-    if can?(current_user, :read_build, project)
+    if project.builds_enabled? && can?(current_user, :read_build, project)
       nav_tabs << :builds
     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 38bc208668398a7ddca950f5b346a094bf89964f..d01b3ae6f050a88b91e996212cab05546811c50d 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -154,6 +154,7 @@ class Ability
         :create_merge_request,
         :create_wiki,
         :manage_builds,
+        :download_build_artifacts,
         :push_code
       ]
     end
@@ -189,7 +190,8 @@ class Ability
         :change_visibility_level,
         :rename_project,
         :remove_project,
-        :archive_project
+        :archive_project,
+        :remove_fork_project
       ]
     end
 
@@ -231,6 +233,7 @@ class Ability
       if group.has_master?(user) || group.has_owner?(user) || user.admin?
         rules.push(*[
           :create_projects,
+          :admin_milestones
         ])
       end
 
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index c8841178e933079568b33e330775dce0ce21fd27..9e70247ef517dfaf5f944e0aad0d2ba79917ddeb 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -23,6 +23,10 @@
 #  after_sign_out_path          :string(255)
 #  session_expire_delay         :integer          default(10080), not null
 #  import_sources               :text
+#  help_page_text               :text
+#  admin_notification_email     :string(255)
+#  shared_runners_enabled       :boolean          default(TRUE), not null
+#  max_artifacts_size           :integer          default(100), not null
 #
 
 class ApplicationSetting < ActiveRecord::Base
@@ -44,6 +48,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|
@@ -64,8 +72,14 @@ class ApplicationSetting < ActiveRecord::Base
     end
   end
 
+  after_commit do
+    Rails.cache.write('application_setting.last', self)
+  end
+
   def self.current
-    ApplicationSetting.last
+    Rails.cache.fetch('application_setting.last') do
+      ApplicationSetting.last
+    end
   end
 
   def self.create_from_defaults
@@ -83,7 +97,9 @@ 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'],
+      max_artifacts_size: Settings.gitlab_ci['max_artifacts_size'],
     )
   end
 
diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb
index 0cf496f7d81490d78b47b23d0cb514ce4e5b5c2d..1307fa0b472def2dd758f3c15ee50c47dd43a473 100644
--- a/app/models/ci/application_setting.rb
+++ b/app/models/ci/application_setting.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: application_settings
+# Table name: ci_application_settings
 #
 #  id                :integer          not null, primary key
 #  all_broken_builds :boolean
@@ -12,9 +12,15 @@
 module Ci
   class ApplicationSetting < ActiveRecord::Base
     extend Ci::Model
-    
+
+    after_commit do
+      Rails.cache.write('ci_application_setting.last', self)
+    end
+
     def self.current
-      Ci::ApplicationSetting.last
+      Rails.cache.fetch('ci_application_setting.last') do
+        Ci::ApplicationSetting.last
+      end
     end
 
     def self.create_from_defaults
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5f8d44148cadc3152a49c87e290214f5426e58fc..e78b154084b014cc4c773b7f42b9a12c4c163624 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: builds
+# Table name: ci_builds
 #
 #  id                 :integer          not null, primary key
 #  project_id         :integer
@@ -11,16 +11,24 @@
 #  updated_at         :datetime
 #  started_at         :datetime
 #  runner_id          :integer
-#  commit_id          :integer
 #  coverage           :float
+#  commit_id          :integer
 #  commands           :text
 #  job_id             :integer
 #  name               :string(255)
+#  deploy             :boolean          default(FALSE)
 #  options            :text
 #  allow_failure      :boolean          default(FALSE), not null
 #  stage              :string(255)
-#  deploy             :boolean          default(FALSE)
 #  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
 #
 
 module Ci
@@ -39,6 +47,8 @@ module Ci
     scope :ignore_failures, ->() { where(allow_failure: false) }
     scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
 
+    mount_uploader :artifacts_file, ArtifactUploader
+
     acts_as_taggable
 
     # To prevent db load megabytes of data from trace
@@ -93,10 +103,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 +116,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 || ''
@@ -212,6 +227,14 @@ module Ci
       "#{dir_to_trace}/#{id}.log"
     end
 
+    def token
+      project.token
+    end
+
+    def valid_token? token
+      project.valid_token? token
+    end
+
     def target_url
       Gitlab::Application.routes.url_helpers.
         namespace_project_build_url(gl_project.namespace, gl_project, self)
@@ -225,7 +248,7 @@ 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
@@ -243,6 +266,13 @@ module Ci
       pending? && !any_runners_online?
     end
 
+    def download_url
+      if artifacts_file.exists?
+        Gitlab::Application.routes.url_helpers.
+          download_namespace_project_build_path(gl_project.namespace, gl_project, self)
+      end
+    end
+
     private
 
     def yaml_variables
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index cd45366b34e595e5a8bc0eb170856762f958c7fb..33b57173928a3fbe8ddcab0889249b255a522958 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -1,18 +1,19 @@
 # == Schema Information
 #
-# Table name: commits
+# Table name: ci_commits
 #
-#  id           :integer          not null, primary key
-#  project_id   :integer
-#  ref          :string(255)
-#  sha          :string(255)
-#  before_sha   :string(255)
-#  push_data    :text
-#  created_at   :datetime
-#  updated_at   :datetime
-#  tag          :boolean          default(FALSE)
-#  yaml_errors  :text
-#  committed_at :datetime
+#  id            :integer          not null, primary key
+#  project_id    :integer
+#  ref           :string(255)
+#  sha           :string(255)
+#  before_sha    :string(255)
+#  push_data     :text
+#  created_at    :datetime
+#  updated_at    :datetime
+#  tag           :boolean          default(FALSE)
+#  yaml_errors   :text
+#  committed_at  :datetime
+#  gl_project_id :integer
 #
 
 module Ci
@@ -91,19 +92,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
 
@@ -132,24 +142,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?
@@ -195,7 +188,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
@@ -219,16 +212,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/event.rb b/app/models/ci/event.rb
index cac3a7a49c1222f1bb91da463e38590ab89176c3..8c39be4267767d52a6e69f2f78035a7417001fec 100644
--- a/app/models/ci/event.rb
+++ b/app/models/ci/event.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: events
+# Table name: ci_events
 #
 #  id          :integer          not null, primary key
 #  project_id  :integer
diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb
index eb65c773570c4e937205b4071ec7c26ba9462f64..669ee1cc0d2d3b6d46a7aacee74fde6c5153a26c 100644
--- a/app/models/ci/project.rb
+++ b/app/models/ci/project.rb
@@ -1,9 +1,9 @@
 # == Schema Information
 #
-# Table name: projects
+# Table name: ci_projects
 #
 #  id                       :integer          not null, primary key
-#  name                     :string(255)      not null
+#  name                     :string(255)
 #  timeout                  :integer          default(3600), not null
 #  created_at               :datetime
 #  updated_at               :datetime
@@ -66,30 +66,6 @@ module Ci
     class << self
       include Ci::CurrentSettings
 
-      def base_build_script
-        <<-eos
-  git submodule update --init
-  ls -la
-        eos
-      end
-
-      def parse(project)
-        params = {
-          gitlab_id:                project.id,
-          default_ref:              project.default_branch || 'master',
-          email_add_pusher:         current_application_settings.add_pusher,
-          email_only_broken_builds: current_application_settings.all_broken_builds,
-        }
-
-        project = Ci::Project.new(params)
-        project.build_missing_services
-        project
-      end
-
-      def already_added?(project)
-        where(gitlab_id: project.id).any?
-      end
-
       def unassigned(runner)
         joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \
           "AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}").
@@ -99,6 +75,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
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 1b3669f1b7a7b5ef88b68215f2b6bb364b9aa575..89710485811202a5b6bf3649177c0483f2bb997b 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: runners
+# Table name: ci_runners
 #
 #  id           :integer          not null, primary key
 #  token        :string(255)
@@ -36,6 +36,7 @@ module Ci
     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
 
diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb
index 44453ee4b41f230be3cb13b3a2bab45aac61700e..3f4fc43873eb2844793d4e6af0ebd246a4e64965 100644
--- a/app/models/ci/runner_project.rb
+++ b/app/models/ci/runner_project.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: runner_projects
+# Table name: ci_runner_projects
 #
 #  id         :integer          not null, primary key
 #  runner_id  :integer          not null
diff --git a/app/models/ci/service.rb b/app/models/ci/service.rb
index ed5e3f940b66548fc4c15b6216ea37f2f821c096..8063c51e82b27861e3e0e4909a0e13ee4fb97776 100644
--- a/app/models/ci/service.rb
+++ b/app/models/ci/service.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index fe224b7dc706ce55dfb88de289efef9e1412fbde..b73c35d5ae507156eb037aea782bc01535a48055 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: triggers
+# Table name: ci_triggers
 #
 #  id         :integer          not null, primary key
 #  token      :string(255)
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index 29cd95533940dd5ae9b6e40b445aa7e88e91afa9..9973d2e5ade0a87e4f21d2107698c807ad3aae4c 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: trigger_requests
+# Table name: ci_trigger_requests
 #
 #  id         :integer          not null, primary key
 #  trigger_id :integer          not null
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 7a542802fa6a0eb967fe5ba40c61920e7cd9c05e..b3d2b809e03c659a03df3a359fe385f453303d9b 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: variables
+# Table name: ci_variables
 #
 #  id                   :integer          not null, primary key
 #  project_id           :integer          not null
diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb
index 8f03b0625da39ee5276071a0944631f318b5ea56..7ca16a1bde82fc7f2d603fe010d48ba6d844d4c5 100644
--- a/app/models/ci/web_hook.rb
+++ b/app/models/ci/web_hook.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: web_hooks
+# Table name: ci_web_hooks
 #
 #  id         :integer          not null, primary key
 #  url        :string(255)      not null
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 0b71838d51537dbf85cbd4638fe60608450391b3..e70f4d37184adea8d9664a2aa6efb8c17932c6b8 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+#  id                 :integer          not null, primary key
+#  project_id         :integer
+#  status             :string(255)
+#  finished_at        :datetime
+#  trace              :text
+#  created_at         :datetime
+#  updated_at         :datetime
+#  started_at         :datetime
+#  runner_id          :integer
+#  coverage           :float
+#  commit_id          :integer
+#  commands           :text
+#  job_id             :integer
+#  name               :string(255)
+#  deploy             :boolean          default(FALSE)
+#  options            :text
+#  allow_failure      :boolean          default(FALSE), not null
+#  stage              :string(255)
+#  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
+#
+
 class CommitStatus < ActiveRecord::Base
   self.table_name = 'ci_builds'
 
@@ -15,12 +48,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 :finished, -> { where(status:[:success, :failed, :canceled]) }
+  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
@@ -28,7 +60,7 @@ class CommitStatus < ActiveRecord::Base
     end
 
     event :drop do
-      transition running: :failed
+      transition [:pending, :running] => :failed
     end
 
     event :success do
@@ -93,4 +125,8 @@ class CommitStatus < ActiveRecord::Base
   def show_warning?
     false
   end
+
+  def download_url
+    nil
+  end
 end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 0e8bcc1a4ece30b363b26d6c05db5396ff6d5026..492a026add9ffdc2fd01420216a970adc850aa92 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"
@@ -24,7 +24,7 @@ module Issuable
 
     scope :authored, ->(user) { where(author_id: user) }
     scope :assigned_to, ->(u) { where(assignee_id: u.id)}
-    scope :recent, -> { order("created_at DESC") }
+    scope :recent, -> { reorder(id: :desc) }
     scope :assigned, -> { where("assignee_id IS NOT NULL") }
     scope :unassigned, -> { where("assignee_id IS NULL") }
     scope :of_projects, ->(ids) { where(project_id: ids) }
@@ -47,8 +47,7 @@ module Issuable
              prefix: true
 
     attr_mentionable :title, :description
-
-    participant :author, :assignee, :notes_with_associations, :mentioned_users
+    participant :author, :assignee, :notes_with_associations
   end
 
   module ClassMethods
@@ -86,6 +85,10 @@ module Issuable
     assignee_id_changed?
   end
 
+  def open?
+    opened? || reopened?
+  end
+
   #
   # Votes
   #
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index b34def66d2e260955609e26e8a0251454da5eb0d..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,22 +47,22 @@ module Mentionable
     self
   end
 
-  def all_references(current_user = self.author, text = self.mentionable_text)
-    ext = Gitlab::ReferenceExtractor.new(self.project, current_user)
+  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)
-    all_references(current_user).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 referenced_mentionables(current_user = self.author, text = self.mentionable_text)
+  def referenced_mentionables(current_user = self.author, text = self.mentionable_text, load_lazy_references: true)
     return [] if text.blank?
 
-    refs = all_references(current_user, text)
-    (refs.issues + refs.merge_requests + refs.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+.
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index ffc874357fdf4b3c101f4e5a97cba5e7cab6f549..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,33 +37,39 @@ 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)
-    self.class.participant_attrs.flat_map do |attr|
-      meth = method(attr)
-
+  def participants(current_user = self.author, load_lazy_references: true)
+    participants = self.class.participant_attrs.flat_map do |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)
-    end.compact.uniq.select do |user|
-      user.can?(:read_project, self.project)
+    end.compact.uniq
+
+    if load_lazy_references
+      participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq
+
+      participants.select! do |user|
+        user.can?(:read_project, project)
+      end
     end
+
+    participants
   end
 
   private
 
   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) }
     when Participable
-      value.participants(current_user)
+      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/event.rb b/app/models/event.rb
index 47600c57e35d38dedfffb2e8ec47a55add4de504..bf64ac29d325759507834eef8be2c3c6d81c36dc 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -45,7 +45,7 @@ class Event < ActiveRecord::Base
   after_create :reset_project_activity
 
   # Scopes
-  scope :recent, -> { order(created_at: :desc) }
+  scope :recent, -> { reorder(id: :desc) }
   scope :code_push, -> { where(action: PUSHED) }
   scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
   scope :with_associations, -> { includes(project: :namespace) }
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
index fa54e3540d07bd99637c76b65d7b53ba4acc58c5..12c934e24949af9494f1247200d7f7b55c88da94 100644
--- a/app/models/generic_commit_status.rb
+++ b/app/models/generic_commit_status.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+#  id                 :integer          not null, primary key
+#  project_id         :integer
+#  status             :string(255)
+#  finished_at        :datetime
+#  trace              :text
+#  created_at         :datetime
+#  updated_at         :datetime
+#  started_at         :datetime
+#  runner_id          :integer
+#  coverage           :float
+#  commit_id          :integer
+#  commands           :text
+#  job_id             :integer
+#  name               :string(255)
+#  deploy             :boolean          default(FALSE)
+#  options            :text
+#  allow_failure      :boolean          default(FALSE), not null
+#  stage              :string(255)
+#  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
+#
+
 class GenericCommitStatus < CommitStatus
   before_validation :set_default_values
 
diff --git a/app/models/global_label.rb b/app/models/global_label.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0171f7d54b7d6a56157d4bdcd4a1b664aba71fd6
--- /dev/null
+++ b/app/models/global_label.rb
@@ -0,0 +1,17 @@
+class GlobalLabel
+  attr_accessor :title, :labels
+  alias_attribute :name, :title
+
+  def self.build_collection(labels)
+    labels = labels.group_by(&:title)
+
+    labels.map do |title, label|
+      new(title, label)
+    end
+  end
+
+  def initialize(title, labels)
+    @title = title
+    @labels = labels
+  end
+end
diff --git a/app/models/group_milestone.rb b/app/models/global_milestone.rb
similarity index 75%
rename from app/models/group_milestone.rb
rename to app/models/global_milestone.rb
index 1dd2be68ebf404bcb4f82b48785ef704d2521d0a..1321ccd963fb449491967fe1365c32394899e56a 100644
--- a/app/models/group_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -1,24 +1,24 @@
-class GroupMilestone
-
+class GlobalMilestone
+  attr_accessor :title, :milestones
   alias_attribute :name, :title
 
+  def self.build_collection(milestones)
+    milestones = milestones.group_by(&:title)
+
+    milestones.map do |title, milestones|
+      new(title, milestones)
+    end
+  end
+
   def initialize(title, milestones)
     @title = title
     @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
@@ -68,15 +68,15 @@ class GroupMilestone
   end
 
   def issues
-    @group_issues ||= milestones.map(&:issues).flatten.group_by(&:state)
+    @issues ||= milestones.map(&:issues).flatten.group_by(&:state)
   end
 
   def merge_requests
-    @group_merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state)
+    @merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state)
   end
 
   def participants
-    @group_participants ||= milestones.map(&:participants).flatten.compact.uniq
+    @participants ||= milestones.map(&:participants).flatten.compact.uniq
   end
 
   def opened_issues
@@ -94,4 +94,8 @@ class GroupMilestone
   def closed_merge_requests
     merge_requests.values_at("closed", "merged", "locked").compact.flatten
   end
+
+  def complete?
+    total_items_count == closed_items_count
+  end
 end
diff --git a/app/models/group.rb b/app/models/group.rb
index 465c22d23ac85ae8305112bf9f637fac3693f336..793a3b5ef2e4a92faa8512510d5bf90b898dff34 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -11,6 +11,7 @@
 #  type        :string(255)
 #  description :string(255)      default(""), not null
 #  avatar      :string(255)
+#  public      :boolean          default(FALSE)
 #
 
 require 'carrierwave/orm/activerecord'
@@ -120,7 +121,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/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index ca7066b959aae42ff43a5924d24e179df7775a23..337b30971260d33555816c8f9ed0661799c849b6 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -2,18 +2,19 @@
 #
 # Table name: web_hooks
 #
-#  id                    :integer          not null, primary key
-#  url                   :string(255)
-#  project_id            :integer
-#  created_at            :datetime
-#  updated_at            :datetime
-#  type                  :string(255)      default("ProjectHook")
-#  service_id            :integer
-#  push_events           :boolean          default(TRUE), not null
-#  issues_events         :boolean          default(FALSE), not null
-#  merge_requests_events :boolean          default(FALSE), not null
-#  tag_push_events       :boolean          default(FALSE)
-#  note_events           :boolean          default(FALSE), not null
+#  id                      :integer          not null, primary key
+#  url                     :string(255)
+#  project_id              :integer
+#  created_at              :datetime
+#  updated_at              :datetime
+#  type                    :string(255)      default("ProjectHook")
+#  service_id              :integer
+#  push_events             :boolean          default(TRUE), not null
+#  issues_events           :boolean          default(FALSE), not null
+#  merge_requests_events   :boolean          default(FALSE), not null
+#  tag_push_events         :boolean          default(FALSE)
+#  note_events             :boolean          default(FALSE), not null
+#  enable_ssl_verification :boolean          default(TRUE)
 #
 
 class ProjectHook < WebHook
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index b55e217975f90c911a27ccffadb5ee25f70e3f34..09bb3ee52a2dc8d82207a550f2602f8a8830f88c 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -2,18 +2,19 @@
 #
 # Table name: web_hooks
 #
-#  id                    :integer          not null, primary key
-#  url                   :string(255)
-#  project_id            :integer
-#  created_at            :datetime
-#  updated_at            :datetime
-#  type                  :string(255)      default("ProjectHook")
-#  service_id            :integer
-#  push_events           :boolean          default(TRUE), not null
-#  issues_events         :boolean          default(FALSE), not null
-#  merge_requests_events :boolean          default(FALSE), not null
-#  tag_push_events       :boolean          default(FALSE)
-#  note_events           :boolean          default(FALSE), not null
+#  id                      :integer          not null, primary key
+#  url                     :string(255)
+#  project_id              :integer
+#  created_at              :datetime
+#  updated_at              :datetime
+#  type                    :string(255)      default("ProjectHook")
+#  service_id              :integer
+#  push_events             :boolean          default(TRUE), not null
+#  issues_events           :boolean          default(FALSE), not null
+#  merge_requests_events   :boolean          default(FALSE), not null
+#  tag_push_events         :boolean          default(FALSE)
+#  note_events             :boolean          default(FALSE), not null
+#  enable_ssl_verification :boolean          default(TRUE)
 #
 
 class ServiceHook < WebHook
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index 6fb2d4210268afb9a5be7c9d7b24cf20b9d77a89..2f63c59b07eaad722898ac369d51d0786844875d 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -2,18 +2,19 @@
 #
 # Table name: web_hooks
 #
-#  id                    :integer          not null, primary key
-#  url                   :string(255)
-#  project_id            :integer
-#  created_at            :datetime
-#  updated_at            :datetime
-#  type                  :string(255)      default("ProjectHook")
-#  service_id            :integer
-#  push_events           :boolean          default(TRUE), not null
-#  issues_events         :boolean          default(FALSE), not null
-#  merge_requests_events :boolean          default(FALSE), not null
-#  tag_push_events       :boolean          default(FALSE)
-#  note_events           :boolean          default(FALSE), not null
+#  id                      :integer          not null, primary key
+#  url                     :string(255)
+#  project_id              :integer
+#  created_at              :datetime
+#  updated_at              :datetime
+#  type                    :string(255)      default("ProjectHook")
+#  service_id              :integer
+#  push_events             :boolean          default(TRUE), not null
+#  issues_events           :boolean          default(FALSE), not null
+#  merge_requests_events   :boolean          default(FALSE), not null
+#  tag_push_events         :boolean          default(FALSE)
+#  note_events             :boolean          default(FALSE), not null
+#  enable_ssl_verification :boolean          default(TRUE)
 #
 
 class SystemHook < WebHook
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index a078accbdbdca9a74b3f2c1a6e534506f839d19c..d6c6f415c4a1fd23e27686222589dbd70c046cb6 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -2,18 +2,19 @@
 #
 # Table name: web_hooks
 #
-#  id                    :integer          not null, primary key
-#  url                   :string(255)
-#  project_id            :integer
-#  created_at            :datetime
-#  updated_at            :datetime
-#  type                  :string(255)      default("ProjectHook")
-#  service_id            :integer
-#  push_events           :boolean          default(TRUE), not null
-#  issues_events         :boolean          default(FALSE), not null
-#  merge_requests_events :boolean          default(FALSE), not null
-#  tag_push_events       :boolean          default(FALSE)
-#  note_events           :boolean          default(FALSE), not null
+#  id                      :integer          not null, primary key
+#  url                     :string(255)
+#  project_id              :integer
+#  created_at              :datetime
+#  updated_at              :datetime
+#  type                    :string(255)      default("ProjectHook")
+#  service_id              :integer
+#  push_events             :boolean          default(TRUE), not null
+#  issues_events           :boolean          default(FALSE), not null
+#  merge_requests_events   :boolean          default(FALSE), not null
+#  tag_push_events         :boolean          default(FALSE)
+#  note_events             :boolean          default(FALSE), not null
+#  enable_ssl_verification :boolean          default(TRUE)
 #
 
 class WebHook < ActiveRecord::Base
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/label.rb b/app/models/label.rb
index 1bb4b5f55cfa6e89f32e3d354e0abd9b3ad6c83a..b306aecbac1250d37d15b7f0eaa73c4e78393146 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -8,6 +8,7 @@
 #  project_id :integer
 #  created_at :datetime
 #  updated_at :datetime
+#  template   :boolean          default(FALSE)
 #
 
 class Label < ActiveRecord::Base
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3c1426f59d08c085e17e92fdc0d8dc0a94af9e99
--- /dev/null
+++ b/app/models/lfs_object.rb
@@ -0,0 +1,8 @@
+class LfsObject < ActiveRecord::Base
+  has_many :lfs_objects_projects, dependent: :destroy
+  has_many :projects, through: :lfs_objects_projects
+
+  validates :oid, presence: true, uniqueness: true
+
+  mount_uploader :file, LfsObjectUploader
+end
diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0fd5f089db9076d03d7c17fcea5518964d688809
--- /dev/null
+++ b/app/models/lfs_objects_project.rb
@@ -0,0 +1,8 @@
+class LfsObjectsProject < ActiveRecord::Base
+  belongs_to :project
+  belongs_to :lfs_object
+
+  validates :lfs_object_id, presence: true
+  validates :lfs_object_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
+  validates :project_id, presence: true
+end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index c83b15c7d392d355cf166a7358d21f6aac88d5d1..2eb03b8ba5b1109c770fdbd46a3ae7986aa337b9 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -20,6 +20,7 @@
 #  position          :integer          default(0)
 #  locked_at         :datetime
 #  updated_by_id     :integer
+#  merge_error       :string(255)
 #
 
 require Rails.root.join("app/models/commit")
@@ -40,7 +41,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, :diffs_no_whitespace, 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 +158,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 +235,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 +258,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 +303,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 +471,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..c499a4b5b4c8006f834fcf3ffcaee513d89120ec 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -19,7 +19,7 @@ class MergeRequestDiff < ActiveRecord::Base
   # Prevent store of diff if commits amount more then 500
   COMMITS_SAFE_SIZE = 500
 
-  attr_reader :commits, :diffs
+  attr_reader :commits, :diffs, :diffs_no_whitespace
 
   belongs_to :merge_request
 
@@ -47,6 +47,20 @@ class MergeRequestDiff < ActiveRecord::Base
     @diffs ||= (load_diffs(st_diffs) || [])
   end
 
+  def diffs_no_whitespace
+    # Get latest sha of branch from source project
+    source_sha = merge_request.source_project.commit(source_branch).sha
+
+    compare_result = Gitlab::CompareResult.new(
+      Gitlab::Git::Compare.new(
+        merge_request.target_project.repository.raw_repository,
+        merge_request.target_branch,
+        source_sha,
+      ), { ignore_whitespace_change: true }
+    )
+    @diffs_no_whitespace ||= load_diffs(dump_commits(compare_result.diffs))
+  end
+
   def commits
     @commits ||= load_commits(st_commits || [])
   end
@@ -55,6 +69,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 +181,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 5782e649f8be15100eadec612886b6ea2f26e5ae..20b92e68d6100176484870b310972546b7af954a 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -11,6 +11,7 @@
 #  type        :string(255)
 #  description :string(255)      default(""), not null
 #  avatar      :string(255)
+#  public      :boolean          default(FALSE)
 #
 
 class Namespace < ActiveRecord::Base
diff --git a/app/models/note.rb b/app/models/note.rb
index ebbdd2f33a127eebe57db56ab8743c927cd84bd2..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
diff --git a/app/models/project.rb b/app/models/project.rb
index 88cd88dcb5a139b05685cdbf32da56bfd32227fc..9ea0d15497acb25833a9d56c679e5ea1ce9e1f09 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
@@ -51,6 +52,7 @@ class Project < ActiveRecord::Base
   default_value_for :visibility_level, gitlab_config_features.visibility_level
   default_value_for :issues_enabled, gitlab_config_features.issues
   default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
+  default_value_for :builds_enabled, gitlab_config_features.builds
   default_value_for :wiki_enabled, gitlab_config_features.wiki
   default_value_for :wall_enabled, false
   default_value_for :snippets_enabled, gitlab_config_features.snippets
@@ -121,6 +123,9 @@ class Project < ActiveRecord::Base
   has_many :starrers, through: :users_star_projects, source: :user
   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_many :lfs_objects_projects, dependent: :destroy
+  has_many :lfs_objects, through: :lfs_objects_projects
 
   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 +248,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
@@ -454,10 +460,6 @@ class Project < ActiveRecord::Base
     list.find { |service| service.to_param == name }
   end
 
-  def gitlab_ci?
-    gitlab_ci_service && gitlab_ci_service.active && gitlab_ci_project.present?
-  end
-
   def ci_services
     services.select { |service| service.category == :ci }
   end
@@ -567,7 +569,7 @@ class Project < ActiveRecord::Base
   end
 
   def empty_repo?
-    !repository.exists? || repository.empty?
+    !repository.exists? || !repository.has_visible_content?
   end
 
   def repo
@@ -774,12 +776,38 @@ 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
+  # TODO: this should be migrated to Project table,
+  # the same as issues_enabled
+  def builds_enabled
+    gitlab_ci_service && gitlab_ci_service.active
+  end
+
+  def builds_enabled?
+    builds_enabled
+  end
+
+  def builds_enabled=(value)
     service = gitlab_ci_service || create_gitlab_ci_service
-    service.active = true
+    service.active = value
     service.save
   end
+
+  def enable_ci
+    self.builds_enabled = true
+  end
+
+  def unlink_fork
+    if forked?
+      forked_from_project.lfs_objects.find_each do |lfs_object|
+        lfs_object.projects << self
+      end
+
+      forked_project_link.destroy
+    end
+  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/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb
index f17993d9f3bc7df6451f1fd2c3d801cfad4b231e..0df03890efb2f838a2f54ba3430ff64ba4f6cdc2 100644
--- a/app/models/project_services/ci/hip_chat_service.rb
+++ b/app/models/project_services/ci/hip_chat_service.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb
index fd1933010015022130be19cefe1a3e36500ea9bf..d31dd6899c1eeb8939ec45eb0d1701872a7d343b 100644
--- a/app/models/project_services/ci/mail_service.rb
+++ b/app/models/project_services/ci/mail_service.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
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/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb
index ee8e49888263c31d1daa95a07c785feaf587576e..7064bfe78db59a2615639b27315da6bf3f7d567b 100644
--- a/app/models/project_services/ci/slack_service.rb
+++ b/app/models/project_services/ci/slack_service.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
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_wiki.rb b/app/models/project_wiki.rb
index 231973fa543353507d42e8d11df59945da4886d4..b5fec38378b9851200eea31ed1283d03527a4cd9 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -86,6 +86,8 @@ class ProjectWiki
     commit = commit_details(:created, message, title)
 
     wiki.write_page(title, format, content, commit)
+
+    update_project_activity
   rescue Gollum::DuplicatePageError => e
     @error_message = "Duplicate page: #{e.message}"
     return false
@@ -95,10 +97,14 @@ class ProjectWiki
     commit = commit_details(:updated, message, page.title)
 
     wiki.update_page(page, page.name, format, content, commit)
+
+    update_project_activity
   end
 
   def delete_page(page, message = nil)
     wiki.delete_page(page, commit_details(:deleted, message, page.title))
+
+    update_project_activity
   end
 
   def page_title_and_dir(title)
@@ -146,4 +152,8 @@ class ProjectWiki
   def path_to_repo
     @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
   end
+
+  def update_project_activity
+    @project.touch(:last_activity_at)
+  end
 end
diff --git a/app/models/release.rb b/app/models/release.rb
new file mode 100644
index 0000000000000000000000000000000000000000..89f70278af5bac889e5af3aa79a4a0de9e8bbbc6
--- /dev/null
+++ b/app/models/release.rb
@@ -0,0 +1,17 @@
+# == Schema Information
+#
+# Table name: releases
+#
+#  id          :integer          not null, primary key
+#  tag         :string(255)
+#  description :text
+#  project_id  :integer
+#  created_at  :datetime
+#  updated_at  :datetime
+#
+
+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 8b51602bc2363c306ce323b13644633fcbc15013..f76b770e867d3ab8754fcdb0b84896d8d570021b 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
@@ -263,20 +296,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'
@@ -312,13 +337,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)
@@ -327,8 +346,8 @@ class Repository
     end
   end
 
-  def branch_names_contains(sha)
-    args = %W(git branch --contains #{sha})
+  def refs_contains_sha(ref_type, sha)
+    args = %W(#{Gitlab.config.git.bin_path} #{ref_type} --contains #{sha})
     names = Gitlab::Popen.popen(args, path_to_repo).first
 
     if names.respond_to?(:split)
@@ -344,21 +363,12 @@ class Repository
     end
   end
 
-  def tag_names_contains(sha)
-    args = %W(git tag --contains #{sha})
-    names = Gitlab::Popen.popen(args, path_to_repo).first
-
-    if names.respond_to?(:split)
-      names = names.split("\n").map(&:strip)
-
-      names.each do |name|
-        name.slice! '* '
-      end
+  def branch_names_contains(sha)
+    refs_contains_sha('branch', sha)
+  end
 
-      names
-    else
-      []
-    end
+  def tag_names_contains(sha)
+    refs_contains_sha('tag', sha)
   end
 
   def branches
@@ -480,9 +490,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
 
@@ -514,7 +528,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/user.rb b/app/models/user.rb
index 17ccb3b87882b318b786b7c118beb5e3904f1c9a..61abea1f6eabbc52e4dbddefc2ee96dc90774b01 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -54,6 +54,7 @@
 #  public_email               :string(255)      default(""), not null
 #  dashboard                  :integer          default(0)
 #  project_view               :integer          default(0)
+#  consumed_timestep          :integer
 #  layout                     :integer          default(0)
 #
 
@@ -183,7 +184,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
 
@@ -235,21 +236,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)
@@ -401,15 +397,26 @@ 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
+
+  def master_or_owner_projects_id
+    @master_or_owner_projects_id ||= begin
+      scope = { access_level: [ Gitlab::Access::MASTER, Gitlab::Access::OWNER ] }
+      project_ids = personal_projects.pluck(:id)
+      project_ids.push(*groups_projects.where(members: scope).pluck(:id))
+      project_ids.push(*projects.where(members: scope).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
@@ -706,12 +713,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
 
@@ -764,12 +774,11 @@ class User < ActiveRecord::Base
     !solo_owned_groups.present?
   end
 
-  def ci_authorized_projects
-    @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects)
-  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: master_or_owner_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 6414b5a0184a987831c24cd1714811ae6cfc6c49..2160bf13e6d281cbc073e3e827d4bdee8d7558f3 100644
--- a/app/services/archive_repository_service.rb
+++ b/app/services/archive_repository_service.rb
@@ -7,7 +7,7 @@ class ArchiveRepositoryService
   end
 
   def execute(options = {})
-    project.repository.clean_old_archives
+    RepositoryArchiveCacheWorker.perform_async
 
     metadata = project.repository.archive_metadata(ref, storage_path, format)
     raise "Repository or ref not found" if metadata.empty?
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/compare_service.rb b/app/services/compare_service.rb
index bfe6a3dc4be2c113cbe1bd5a86033df2379e2361..ec581658fc16d552cc0b4df2a1e26ea2034022d9 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -3,7 +3,7 @@ require 'securerandom'
 # Compare 2 branches for one repo or between repositories
 # and return Gitlab::CompareResult object that responds to commits and diffs
 class CompareService
-  def execute(source_project, source_branch, target_project, target_branch)
+  def execute(source_project, source_branch, target_project, target_branch, diff_options = {})
     source_commit = source_project.commit(source_branch)
     return unless source_commit
 
@@ -25,7 +25,7 @@ class CompareService
         target_project.repository.raw_repository,
         target_branch,
         source_sha,
-      )
+      ), diff_options
     )
   end
 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 81d47602f13cd2fc8ed04c8d0481fdb4cc1fddcc..ccb6b97858c0f0023432b61790f608d7f9dd4cf1 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -49,15 +49,18 @@ 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
     # we enable CI automatically
-    if !project.gitlab_ci? && gitlab_ci_yaml?(newrev)
+    if !project.builds_enabled? && gitlab_ci_yaml?(newrev)
       project.enable_ci
     end
 
@@ -76,7 +79,7 @@ class GitPushService
 
     authors = Hash.new do |hash, commit|
       email = commit.author_email
-      return hash[email] if hash.has_key?(email)
+      next hash[email] if hash.has_key?(email)
 
       hash[email] = commit_user(commit)
     end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 2b5426ad4528f3d33b503840cc4b9c62c88e21be..aa1fd79d22dda88e2a21797a64d55b7518ae9d5e 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -22,24 +22,27 @@ module Issues
             issue, issue.labels - old_labels, old_labels - issue.labels)
         end
 
-        if issue.previous_changes.include?('milestone_id')
-          create_milestone_note(issue)
-        end
+        handle_changes(issue)
+        issue.create_new_cross_references!(current_user)
+        execute_hooks(issue, 'update')
+      end
 
-        if issue.previous_changes.include?('assignee_id')
-          create_assignee_note(issue)
-          notification_service.reassigned_issue(issue, current_user)
-        end
+      issue
+    end
 
-        if issue.previous_changes.include?('title')
-          create_title_change_note(issue, issue.previous_changes['title'].first)
-        end
+    def handle_changes(issue)
+      if issue.previous_changes.include?('milestone_id')
+        create_milestone_note(issue)
+      end
 
-        issue.create_new_cross_references!
-        execute_hooks(issue, 'update')
+      if issue.previous_changes.include?('assignee_id')
+        create_assignee_note(issue)
+        notification_service.reassigned_issue(issue, current_user)
       end
 
-      issue
+      if issue.previous_changes.include?('title')
+        create_title_change_note(issue, issue.previous_changes['title'].first)
+      end
     end
   end
 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 ebbe0af803ba60bc069d7ce10874f18708180ab2..d2849e5193f95ba4c65faa908a309528a45e08aa 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -35,35 +35,38 @@ module MergeRequests
           )
         end
 
-        if merge_request.previous_changes.include?('target_branch')
-          create_branch_change_note(merge_request, 'target',
-                                    merge_request.previous_changes['target_branch'].first,
-                                    merge_request.target_branch)
-        end
+        handle_changes(merge_request)
+        merge_request.create_new_cross_references!(current_user)
+        execute_hooks(merge_request, 'update')
+      end
 
-        if merge_request.previous_changes.include?('milestone_id')
-          create_milestone_note(merge_request)
-        end
+      merge_request
+    end
 
-        if merge_request.previous_changes.include?('assignee_id')
-          create_assignee_note(merge_request)
-          notification_service.reassigned_merge_request(merge_request, current_user)
-        end
+    def handle_changes(merge_request)
+      if merge_request.previous_changes.include?('target_branch')
+        create_branch_change_note(merge_request, 'target',
+                                  merge_request.previous_changes['target_branch'].first,
+                                  merge_request.target_branch)
+      end
 
-        if merge_request.previous_changes.include?('title')
-          create_title_change_note(merge_request, merge_request.previous_changes['title'].first)
-        end
+      if merge_request.previous_changes.include?('milestone_id')
+        create_milestone_note(merge_request)
+      end
 
-        if merge_request.previous_changes.include?('target_branch') ||
-            merge_request.previous_changes.include?('source_branch')
-          merge_request.mark_as_unchecked
-        end
+      if merge_request.previous_changes.include?('assignee_id')
+        create_assignee_note(merge_request)
+        notification_service.reassigned_merge_request(merge_request, current_user)
+      end
 
-        merge_request.create_new_cross_references!
-        execute_hooks(merge_request, 'update')
+      if merge_request.previous_changes.include?('title')
+        create_title_change_note(merge_request, merge_request.previous_changes['title'].first)
       end
 
-      merge_request
+      if merge_request.previous_changes.include?('target_branch') ||
+          merge_request.previous_changes.include?('source_branch')
+        merge_request.mark_as_unchecked
+      end
     end
   end
 end
diff --git a/app/services/milestones/group_service.rb b/app/services/milestones/group_service.rb
deleted file mode 100644
index 11d702f1e7b33753f1ccc4e14294aee7e3b21a15..0000000000000000000000000000000000000000
--- a/app/services/milestones/group_service.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module Milestones
-  class GroupService < Milestones::BaseService
-    def initialize(project_milestones)
-      @project_milestones = project_milestones.group_by(&:title)
-    end
-
-    def execute
-      build(@project_milestones)
-    end
-
-    def milestone(title)
-      if title
-        group_milestone = @project_milestones[title].group_by(&:title)
-        build(group_milestone).first
-      else
-        nil
-      end
-    end
-
-    private
-
-    def build(milestone)
-      milestone.map{ |title, milestones| GroupMilestone.new(title, milestones) }
-    end
-  end
-end
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
index 6c2f08e5963c51e0e2c2f4b44490e7edb638f777..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!
+      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/fork_service.rb b/app/services/projects/fork_service.rb
index 46374a3909a8bd3e7b3165dcb51ef78b174d3d20..5da1c7afd924dfcf19f106320987dfa1f0257b39 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -17,7 +17,7 @@ module Projects
       new_project = CreateService.new(current_user, new_params).execute
 
       if new_project.persisted?
-        if @project.gitlab_ci?
+        if @project.builds_enabled?
           new_project.enable_ci
 
           settings = @project.gitlab_ci_project.attributes.select do |attr_name, value|
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 9a5fe4af9dde7e93b9c298018867b9a52309c15f..8b5143e1eb75a45920bed06dc8ec68e82fd4faab 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -33,17 +33,7 @@ class SystemHooksService
         )
       end
     when Project
-      owner = model.owner
-
-      data.merge!({
-        name: model.name,
-        path: model.path,
-        path_with_namespace: model.path_with_namespace,
-        project_id: model.id,
-        owner_name: owner.name,
-        owner_email: owner.respond_to?(:email) ?  owner.email : "",
-        project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
-      })
+      data.merge!(project_data(model))
     when User
       data.merge!({
         name: model.name,
@@ -51,16 +41,7 @@ class SystemHooksService
         user_id: model.id
       })
     when ProjectMember
-      data.merge!({
-        project_name: model.project.name,
-        project_path: model.project.path,
-        project_path_with_namespace: model.project.path_with_namespace,
-        project_id: model.project.id,
-        user_name: model.user.name,
-        user_email: model.user.email,
-        access_level: model.human_access,
-        project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
-      })
+      data.merge!(project_member_data(model))
     when Group
       owner = model.owner
 
@@ -72,15 +53,7 @@ class SystemHooksService
         owner_email: owner.respond_to?(:email) ? owner.email : nil,
       )
     when GroupMember
-      data.merge!(
-        group_name: model.group.name,
-        group_path: model.group.path,
-        group_id: model.group.id,
-        user_name: model.user.name,
-        user_email: model.user.email,
-        user_id: model.user.id,
-        group_access: model.human_access,
-      )
+      data.merge!(group_member_data(model))
     end
   end
 
@@ -96,4 +69,43 @@ class SystemHooksService
       "#{model.class.name.downcase}_#{event.to_s}"
     end
   end
+
+  def project_data(model)
+    owner = model.owner
+
+    {
+      name: model.name,
+      path: model.path,
+      path_with_namespace: model.path_with_namespace,
+      project_id: model.id,
+      owner_name: owner.name,
+      owner_email: owner.respond_to?(:email) ?  owner.email : "",
+      project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
+    }
+  end
+
+  def project_member_data(model)
+    {
+      project_name: model.project.name,
+      project_path: model.project.path,
+      project_path_with_namespace: model.project.path_with_namespace,
+      project_id: model.project.id,
+      user_name: model.user.name,
+      user_email: model.user.email,
+      access_level: model.human_access,
+      project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
+    }
+  end
+
+  def group_member_data(model)
+    {
+      group_name: model.group.name,
+      group_path: model.group.path,
+      group_id: model.group.id,
+      user_name: model.user.name,
+      user_email: model.user.email,
+      user_id: model.user.id,
+      group_access: model.human_access,
+    }
+  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/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b4e0fc5772dc101fdd760c3e1a95d6ec96cf6aca
--- /dev/null
+++ b/app/uploaders/artifact_uploader.rb
@@ -0,0 +1,50 @@
+# encoding: utf-8
+class ArtifactUploader < CarrierWave::Uploader::Base
+  storage :file
+
+  attr_accessor :build, :field
+
+  def self.artifacts_path
+    File.expand_path('shared/artifacts/', Rails.root)
+  end
+
+  def self.artifacts_upload_path
+    File.expand_path('shared/artifacts/tmp/uploads/', Rails.root)
+  end
+
+  def self.artifacts_cache_path
+    File.expand_path('shared/artifacts/tmp/cache/', Rails.root)
+  end
+
+  def initialize(build, field)
+    @build, @field = build, field
+  end
+
+  def artifacts_path
+    File.join(build.created_at.utc.strftime('%Y_%m'), build.project.id.to_s, build.id.to_s)
+  end
+
+  def store_dir
+    File.join(ArtifactUploader.artifacts_path, artifacts_path)
+  end
+
+  def cache_dir
+    File.join(ArtifactUploader.artifacts_cache_path, artifacts_path)
+  end
+
+  def file_storage?
+    self.class.storage == CarrierWave::Storage::File
+  end
+
+  def exists?
+    file.try(:exists?)
+  end
+
+  def move_to_cache
+    true
+  end
+
+  def move_to_store
+    true
+  end
+end
diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb
index a9691bee46e23f20eca66b96e0577b700769b0e1..a65a896e41e597394003888e50d217c7a5dfc9d8 100644
--- a/app/uploaders/attachment_uploader.rb
+++ b/app/uploaders/attachment_uploader.rb
@@ -1,26 +1,11 @@
 # encoding: utf-8
 
 class AttachmentUploader < CarrierWave::Uploader::Base
+  include UploaderHelper
+
   storage :file
 
   def store_dir
     "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
   end
-
-  def image?
-    img_ext = %w(png jpg jpeg gif bmp tiff)
-    if file.respond_to?(:extension)
-      img_ext.include?(file.extension.downcase)
-    else
-      # Not all CarrierWave storages respond to :extension
-      ext = file.path.split('.').last.downcase
-      img_ext.include?(ext)
-    end
-  rescue
-    false
-  end
-
-  def file_storage?
-    self.class.storage == CarrierWave::Storage::File
-  end
 end
diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
index 7cad044555b1aeae2eacba6b7eb57386fdf565b8..6135c3ad96f4e5c3916f426d08cc177880829a2a 100644
--- a/app/uploaders/avatar_uploader.rb
+++ b/app/uploaders/avatar_uploader.rb
@@ -1,6 +1,8 @@
 # encoding: utf-8
 
 class AvatarUploader < CarrierWave::Uploader::Base
+  include UploaderHelper
+
   storage :file
 
   after :store, :reset_events_cache
@@ -9,23 +11,6 @@ class AvatarUploader < CarrierWave::Uploader::Base
     "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
   end
 
-  def image?
-    img_ext = %w(png jpg jpeg gif bmp tiff)
-    if file.respond_to?(:extension)
-      img_ext.include?(file.extension.downcase)
-    else
-      # Not all CarrierWave storages respond to :extension
-      ext = file.path.split('.').last.downcase
-      img_ext.include?(ext)
-    end
-  rescue
-    false
-  end
-
-  def file_storage?
-    self.class.storage == CarrierWave::Storage::File
-  end
-
   def reset_events_cache(file)
     model.reset_events_cache if model.is_a?(User)
   end
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index e82115858342f4af270fa2d474753a52ed2f2829..ac920119a854fc2b4a973579bffc5733193514a6 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -1,5 +1,7 @@
 # encoding: utf-8
 class FileUploader < CarrierWave::Uploader::Base
+  include UploaderHelper
+
   storage :file
 
   attr_accessor :project, :secret
@@ -28,21 +30,4 @@ class FileUploader < CarrierWave::Uploader::Base
   def secure_url
     File.join("/uploads", @secret, file.filename)
   end
-
-  def file_storage?
-    self.class.storage == CarrierWave::Storage::File
-  end
-
-  def image?
-    img_ext = %w(png jpg jpeg gif bmp tiff)
-    if file.respond_to?(:extension)
-      img_ext.include?(file.extension.downcase)
-    else
-      # Not all CarrierWave storages respond to :extension
-      ext = file.path.split('.').last.downcase
-      img_ext.include?(ext)
-    end
-  rescue
-    false
-  end
 end
diff --git a/app/uploaders/lfs_object_uploader.rb b/app/uploaders/lfs_object_uploader.rb
new file mode 100644
index 0000000000000000000000000000000000000000..28085b310837712ae030988964280158950e0dee
--- /dev/null
+++ b/app/uploaders/lfs_object_uploader.rb
@@ -0,0 +1,29 @@
+# encoding: utf-8
+
+class LfsObjectUploader < CarrierWave::Uploader::Base
+  storage :file
+
+  def store_dir
+    "#{Gitlab.config.lfs.storage_path}/#{model.oid[0,2]}/#{model.oid[2,2]}"
+  end
+
+  def cache_dir
+    "#{Gitlab.config.lfs.storage_path}/tmp/cache"
+  end
+
+  def move_to_cache
+    true
+  end
+
+  def move_to_store
+    true
+  end
+
+  def exists?
+    file.try(:exists?)
+  end
+
+  def filename
+    model.oid[4..-1]
+  end
+end
diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5ef440f3367c3c4f12c2577cd84171f5f1964677
--- /dev/null
+++ b/app/uploaders/uploader_helper.rb
@@ -0,0 +1,19 @@
+# Extra methods for uploader
+module UploaderHelper
+  def image?
+    img_ext = %w(png jpg jpeg gif bmp tiff)
+    if file.respond_to?(:extension)
+      img_ext.include?(file.extension.downcase)
+    else
+      # Not all CarrierWave storages respond to :extension
+      ext = file.path.split('.').last.downcase
+      img_ext.include?(ext)
+    end
+  rescue
+    false
+  end
+
+  def file_storage?
+    self.class.storage == CarrierWave::Storage::File
+  end
+end
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..ddaf0e0e8ffae6b79185aa49caa2f9a9a6e55d98 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,19 @@
         = 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-group
+      = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2'
+      .col-sm-10
+        = f.number_field :max_artifacts_size, class: 'form-control'
+
   .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/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index 4245d0f1eda1f52b3c84c7f830493af0acc87775..8d1cab4137c4950c5c557482a7e3e899b658cac7 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -7,7 +7,7 @@
 
   .pull-right
     - unless @user == current_user
-      = link_to 'Log in as this user', login_as_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info"
+      = link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info"
     = link_to edit_admin_user_path(@user), class: "btn btn-grouped" do
       %i.fa.fa-pencil-square-o
       Edit
diff --git a/app/views/users/_profile.html.haml b/app/views/admin/users/_profile.html.haml
similarity index 83%
rename from app/views/users/_profile.html.haml
rename to app/views/admin/users/_profile.html.haml
index 90d9980c85ca44e9a127a9378f6b7279d5e22694..7d11edc79e25147e6011f02706d36a002a58a377 100644
--- a/app/views/users/_profile.html.haml
+++ b/app/views/admin/users/_profile.html.haml
@@ -16,11 +16,11 @@
     - unless user.linkedin.blank?
       %li
         %span.light LinkedIn:
-        %strong= link_to user.linkedin, "http://www.linkedin.com/in/#{user.linkedin}"
+        %strong= link_to user.linkedin, "https://www.linkedin.com/in/#{user.linkedin}"
     - unless user.twitter.blank?
       %li
         %span.light Twitter:
-        %strong= link_to user.twitter, "http://www.twitter.com/#{user.twitter}"
+        %strong= link_to user.twitter, "https://twitter.com/#{user.twitter}"
     - unless user.website_url.blank?
       %li
         %span.light Website:
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 231bcb0426f0a3f1ff5b0e2c4e302e611de72906..0848504b7a638939cb48f3470374afbfa36099ce 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -14,7 +14,7 @@
           %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/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index 21b25c3986e8a3903f1b0e8c3b0568b71cfdc162..635251e2374e82ceed3bc9b4a31b80489a9fe4b8 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -10,10 +10,10 @@
 
 .milestones
   %ul.content-list
-    - if @dashboard_milestones.blank?
+    - if @milestones.blank?
       %li
         .nothing-here-block No milestones to show
     - else
-      - @dashboard_milestones.each do |milestone|
+      - @milestones.each do |milestone|
         = render 'milestone', milestone: milestone
-  = paginate @dashboard_milestones, theme: "gitlab"
+  = paginate @milestones, theme: "gitlab"
diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml
index d5c4a44fef674ae709d40b8669d2c9cfe7970b90..83077a398bd2fdefa855fc812de5c8c4bda2163f 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -1,82 +1,84 @@
-- page_title @dashboard_milestone.title, "Milestones"
+- page_title @milestone.title, "Milestones"
 %h4.page-title
-  .issue-box{ class: "issue-box-#{@dashboard_milestone.closed? ? 'closed' : 'open'}" }
-    - if @dashboard_milestone.closed?
+  .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
+    - if @milestone.closed?
       Closed
     - else
       Open
-  Milestone #{@dashboard_milestone.title}
+  Milestone #{@milestone.title}
 
 %hr
-- if (@dashboard_milestone.total_items_count == @dashboard_milestone.closed_items_count) && @dashboard_milestone.active?
+- if @milestone.complete? && @milestone.active?
   .alert.alert-success
     %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
+    - @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
     Progress:
-    #{@dashboard_milestone.closed_items_count} closed
+    #{@milestone.closed_items_count} closed
     &ndash;
-    #{@dashboard_milestone.open_items_count} open
-  = milestone_progress_bar(@dashboard_milestone)
+    #{@milestone.open_items_count} open
+  = milestone_progress_bar(@milestone)
 
 %ul.nav.nav-tabs
   %li.active
     = link_to '#tab-issues', 'data-toggle' => 'tab' do
       Issues
-      %span.badge= @dashboard_milestone.issue_count
+      %span.badge= @milestone.issue_count
   %li
     = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do
       Merge Requests
-      %span.badge= @dashboard_milestone.merge_requests_count
+      %span.badge= @milestone.merge_requests_count
   %li
     = link_to '#tab-participants', 'data-toggle' => 'tab' do
       Participants
-      %span.badge= @dashboard_milestone.participants.count
+      %span.badge= @milestone.participants.count
 
   .pull-right
-    = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @dashboard_milestone.title), class: "btn  edit-milestone-link btn-grouped"
+    = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn  edit-milestone-link btn-grouped"
 
 .tab-content
   .tab-pane.active#tab-issues
     .row
       .col-md-6
-        = render 'issues', title: "Open", issues: @dashboard_milestone.opened_issues
+        = render 'issues', title: "Open", issues: @milestone.opened_issues
       .col-md-6
-        = render 'issues', title: "Closed", issues: @dashboard_milestone.closed_issues
+        = render 'issues', title: "Closed", issues: @milestone.closed_issues
 
   .tab-pane#tab-merge-requests
     .row
       .col-md-6
-        = render 'merge_requests', title: "Open", merge_requests: @dashboard_milestone.opened_merge_requests
+        = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests
       .col-md-6
-        = render 'merge_requests', title: "Closed", merge_requests: @dashboard_milestone.closed_merge_requests
+        = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests
 
   .tab-pane#tab-participants
     %ul.bordered-list
-      - @dashboard_milestone.participants.each do |user|
+      - @milestone.participants.each do |user|
         %li
           = link_to user, title: user.name, class: "darken" do
             = image_tag avatar_icon(user, 32), class: "avatar s32"
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/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/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/_milestone.html.haml b/app/views/groups/milestones/_milestone.html.haml
index 41dffdd2fb83fdb3d01e337ebf48c17de5bf6b2a..a20bf75bc39e2d320651479f0a116a9f90935a9c 100644
--- a/app/views/groups/milestones/_milestone.html.haml
+++ b/app/views/groups/milestones/_milestone.html.haml
@@ -22,7 +22,7 @@
             %span.label.label-gray
               = milestone.project.name
     .col-sm-6
-      - if can?(current_user, :admin_group, @group)
+      - if can?(current_user, :admin_milestones, @group)
         - if milestone.closed?
           = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen"
         - else
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 2bbcad5fdfb7f56bf6ca601f96ca438ae438d5d1..84ec77c61888e082a057a9e6e74230bf8520e416 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -3,15 +3,22 @@
 
 = render 'shared/milestones_filter'
 .gray-content-block
-  Only milestones from
-  %strong #{@group.name}
-  group are listed here.
+  - if can?(current_user, :admin_milestones, @group)
+    .pull-right
+      %span.pull-right.hidden-xs
+        = link_to new_group_milestone_path(@group), class: "btn btn-new" do
+          New Milestone
+
+  .oneline
+    Only milestones from
+    %strong #{@group.name}
+    group are listed here.
 .milestones
   %ul.content-list
-    - if @group_milestones.blank?
+    - if @milestones.blank?
       %li
         .nothing-here-block No milestones to show
     - else
-      - @group_milestones.each do |milestone|
+      - @milestones.each do |milestone|
         = render 'milestone', milestone: milestone
-  = paginate @group_milestones, theme: "gitlab"
+  = paginate @milestones, theme: "gitlab"
diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..800bac4ef023478e9d4e84e7a5e1c077738c51f2
--- /dev/null
+++ b/app/views/groups/milestones/new.html.haml
@@ -0,0 +1,48 @@
+- page_title "Milestones"
+- header_title group_title(@group, "Milestones", group_milestones_path(@group))
+
+%h3.page-title
+  New Milestone
+
+%p.light
+  This will create milestone in every selected project
+%hr
+
+= form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-requires-input' }  do |f|
+  .row
+    .col-md-6
+      .form-group
+        = f.label :title, "Title", class: "control-label"
+        .col-sm-10
+          = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true
+          %p.hint Required
+      .form-group.milestone-description
+        = f.label :description, "Description", class: "control-label"
+        .col-sm-10
+          = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
+            = render 'projects/zen', f: f, attr: :description, classes: 'description form-control js-quick-submit'
+            .clearfix
+            .error-alert
+      .form-group
+        = f.label :projects, "Projects", class: "control-label"
+        .col-sm-10
+          = f.collection_select :project_ids, @group.projects, :id, :name,
+            { selected: @group.projects.map(&:id) }, multiple: true, class: 'select2'
+
+    .col-md-6
+      .form-group
+        = f.label :due_date, "Due Date", class: "control-label"
+        .col-sm-10= f.hidden_field :due_date
+        .col-sm-10
+          .datepicker
+
+  .form-actions
+    = f.submit 'Create Milestone', class: "btn-create btn"
+    = link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel"
+
+
+:javascript
+  $(".datepicker").datepicker({
+    dateFormat: "yy-mm-dd",
+    onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
+  }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index c6cde97c585298290e62a4447c711bb8014a544c..d161259e4aa0d9714367116642d78d60df5699a8 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,90 +1,92 @@
-- page_title @group_milestone.title, "Milestones"
+- page_title @milestone.title, "Milestones"
 = render "header_title"
 
 %h4.page-title
-  .issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" }
-    - if @group_milestone.closed?
+  .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
+    - if @milestone.closed?
       Closed
     - else
       Open
-  Milestone #{@group_milestone.title}
+  Milestone #{@milestone.title}
   .pull-right
-    - if can?(current_user, :admin_group, @group)
-      - if @group_milestone.active?
-        = link_to 'Close Milestone', group_milestone_path(@group, @group_milestone.safe_title, title: @group_milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close"
+    - if can?(current_user, :admin_milestones, @group)
+      - if @milestone.active?
+        = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close"
       - else
-        = link_to 'Reopen Milestone', group_milestone_path(@group, @group_milestone.safe_title, title: @group_milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
+        = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
 
 %hr
-- if (@group_milestone.total_items_count == @group_milestone.closed_items_count) && @group_milestone.active?
+- if @milestone.complete? && @milestone.active?
   .alert.alert-success
     %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
+    - @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
     Progress:
-    #{@group_milestone.closed_items_count} closed
+    #{@milestone.closed_items_count} closed
     &ndash;
-    #{@group_milestone.open_items_count} open
-  = milestone_progress_bar(@group_milestone)
+    #{@milestone.open_items_count} open
+  = milestone_progress_bar(@milestone)
 
 %ul.nav.nav-tabs
   %li.active
     = link_to '#tab-issues', 'data-toggle' => 'tab' do
       Issues
-      %span.badge= @group_milestone.issue_count
+      %span.badge= @milestone.issue_count
   %li
     = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do
       Merge Requests
-      %span.badge= @group_milestone.merge_requests_count
+      %span.badge= @milestone.merge_requests_count
   %li
     = link_to '#tab-participants', 'data-toggle' => 'tab' do
       Participants
-      %span.badge= @group_milestone.participants.count
+      %span.badge= @milestone.participants.count
 
   .pull-right
-    = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @group_milestone.title), class: "btn  edit-milestone-link btn-grouped"
+    = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn  edit-milestone-link btn-grouped"
 
 .tab-content
   .tab-pane.active#tab-issues
     .row
       .col-md-6
-        = render 'issues', title: "Open", issues: @group_milestone.opened_issues
+        = render 'issues', title: "Open", issues: @milestone.opened_issues
       .col-md-6
-        = render 'issues', title: "Closed", issues: @group_milestone.closed_issues
+        = render 'issues', title: "Closed", issues: @milestone.closed_issues
 
   .tab-pane#tab-merge-requests
     .row
       .col-md-6
-        = render 'merge_requests', title: "Open", merge_requests: @group_milestone.opened_merge_requests
+        = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests
       .col-md-6
-        = render 'merge_requests', title: "Closed", merge_requests: @group_milestone.closed_merge_requests
+        = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests
 
   .tab-pane#tab-participants
     %ul.bordered-list
-      - @group_milestone.participants.each do |user|
+      - @milestone.participants.each do |user|
         %li
           = link_to user, title: user.name, class: "darken" do
             = image_tag avatar_icon(user, 32), class: "avatar s32"
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 67349fcbd787bf437315ba88e83cb55138c06d47..7e801b5332d9cc848a98e92f51c8e84db0bed059 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -222,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/_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/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..3ca30d3baabd89114d18b52495d25f56678c08f5 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -13,6 +13,10 @@
           %li.visible-sm.visible-xs
             = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
               = icon('search')
+          - if session[:impersonator_id]
+            %li.impersonation
+              = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop impersonation', data: { toggle: 'tooltip', placement: 'bottom' } do
+                = icon('user-secret fw')
           - if current_user.is_admin?
             %li
               = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do
@@ -21,6 +25,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 53a913fe8f379e696555e05a8ef75d8febf0f8cc..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,7 +32,7 @@
           Files
 
   - if project_nav_tab? :commits
-    = nav_link(controller: %w(commit commits compare repositories tags branches)) 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
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index 954dbe5d2b9079c39f6fd76c1ee8bf8545e9c6b0..377a99e719aa4bfb18784b2699f54c63a0151c39 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
 
@@ -34,7 +34,7 @@
         %span
           Protected Branches
 
-    - if @project.gitlab_ci?
+    - if @project.builds_enabled?
       = nav_link(controller: :runners) do
         = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners', data: {placement: 'right'} do
           = icon('cog fw')
@@ -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..3ca4c340406511d7488e76997834f7dd75e0aa64 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -40,5 +40,10 @@
             Reply to this email directly or
             #{link_to "view it on GitLab", @target_url}.
           - else
-            #{link_to "View it on GitLab", @target_url}
-          = email_action @target_url
+            #{link_to "View it on GitLab", @target_url}.
+          %br
+          -# Don't link the host is the line below, one link in the email is easier to quickly click than two.
+          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
\ No newline at end of file
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/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb
index d8a23dabf499789a10a626814d047c9a7cf9e68c..b2c5f71e46505e16aaa8c2c0342e46bdd5138768 100644
--- a/app/views/notify/project_was_moved_email.text.erb
+++ b/app/views/notify/project_was_moved_email.text.erb
@@ -1,4 +1,4 @@
-Project #{@old_path_with_namespace} was moved to another location
+Project <%= @old_path_with_namespace %> was moved to another location
 
 The project is now located under 
 <%= namespace_project_url(@project.namespace, @project) %>
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/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/_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/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/_build.html.haml b/app/views/projects/builds/_build.html.haml
deleted file mode 100644
index 4ce4ed63b401d6410cb21c181019a383cee0989a..0000000000000000000000000000000000000000
--- a/app/views/projects/builds/_build.html.haml
+++ /dev/null
@@ -1,53 +0,0 @@
-%tr.build
-  %td.status
-    = ci_status_with_icon(build.status)
-
-  %td.commit_status-link
-    - if build.target_url
-      = link_to build.target_url do
-        %strong Build ##{build.id}
-    - else
-      %strong Build ##{build.id}
-
-    - if build.show_warning?
-      %i.fa.fa-warning.text-warning
-
-  %td
-    = link_to build.short_sha, namespace_project_commit_path(@project.namespace, @project, build.sha)
-
-  %td
-    = link_to build.ref, namespace_project_commits_path(@project.namespace, @project, build.ref)
-
-  %td
-    - if build.runner
-      = runner_link(build.runner)
-    - else
-      .light none
-
-  %td
-    = build.name
-
-    .pull-right
-      - if build.tags.any?
-        - build.tags.each do |tag|
-          %span.label.label-primary
-            = tag
-      - if build.trigger_request
-        %span.label.label-info triggered
-      - if build.allow_failure
-        %span.label.label-danger allowed to fail
-
-  %td.duration
-    - if build.duration
-      #{duration_in_words(build.finished_at, build.started_at)}
-
-  %td.timestamp
-    - if build.finished_at
-      %span #{time_ago_in_words build.finished_at} ago
-
-  %td
-    .pull-right
-      - if current_user && can?(current_user, :manage_builds, @project)
-        - if build.cancel_url
-          = link_to build.cancel_url, title: 'Cancel' do
-            %i.fa.fa-remove.cred
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
index 4d8ca16d98a4e060ac22773a6645762445658d05..dab7164153ff119783297a10e48d2f53794aff2e 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -1,12 +1,12 @@
 - page_title "Builds"
-- header_title project_title(@project, "Builds", project_builds_path(@project))
+= 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'
+          = 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?)}
@@ -25,28 +25,29 @@
         %span.badge.js-totalbuilds-count= @all_builds.count(:id)
 
 .gray-content-block
-  List of #{@scope || 'running'} builds from this project
+  #{(@scope || 'running').capitalize} builds from this project
 
 %ul.content-list
   - if @builds.blank?
     %li
       .nothing-here-block No builds to show
   - else
-    %table.table.builds
-      %thead
-        %tr
-          %th Status
-          %th Build ID
-          %th Commit
-          %th Ref
-          %th Runner
-          %th Name
-          %th Duration
-          %th Finished at
-          %th
-
-      - @builds.each do |build|
-        = render 'projects/builds/build', build: build
-
-    = paginate @builds
+    .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 c45bfb27b8f5eb36c9903c0ee2092473c8b84c40..907e1ce10bddaf600fa7d406abad12505e164b44 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,7 +23,7 @@
                 = build.id
 
 
-      - unless @commit.latest_builds_for_ref(@build.ref).include?(@build)
+      - if @build.retried?
         %li.active
           %a
             Build ##{@build.id}
@@ -37,7 +40,7 @@
             %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?
@@ -84,16 +87,19 @@
             Test coverage
           %h1 #{@build.coverage}%
 
+      - if current_user && can?(current_user, :download_build_artifacts, @project) && @build.download_url
+        .build-widget.center
+          = link_to "Download artifacts", @build.download_url, class: 'btn btn-sm btn-primary'
 
       .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
@@ -101,15 +107,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}
 
@@ -134,10 +140,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}
@@ -155,14 +162,16 @@
 
       - 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
                 %td
                   = ci_icon_for_status(build.status)
                 %td
-                  = link_to namespace_project_build_path(@project.namespace, @project, @build) do
+                  = link_to namespace_project_build_path(@project.namespace, @project, build) do
                     - if build.name
                       = build.name
                     - else
@@ -171,8 +180,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..18cae8ef6d348782733f0a1cb96bad9fa9a01a40 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -5,7 +5,7 @@
     %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
       - if can?(current_user, :create_issue, @project)
         %li
-          = link_to url_for_new_issue do
+          = link_to url_for_new_issue(@project, only_path: true) do
             = icon('exclamation-circle fw')
             New issue
       - if can?(current_user, :create_merge_request, @project)
@@ -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..acc912d459606c5831bf20246ad1f8c2eaeefe2a 100644
--- a/app/views/projects/ci_settings/edit.html.haml
+++ b/app/views/projects/ci_settings/edit.html.haml
@@ -1,24 +1,6 @@
-- 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)}
-    or
-    %a.preview-yml{:href => "#yaml-content", "data-toggle" => "modal"} preview
-    yaml file which is based on your old jobs.
-    Put this file to the root of your project and name it .gitlab-ci.yml
+- page_title "CI Settings"
 
 - if no_runners_for_project?(@ci_project)
   = render 'no_runners'
 
 = render 'form'
-
-- if @ci_project.generated_yaml_config
-  #yaml-content.modal.fade{"aria-hidden" => "true", "aria-labelledby" => ".gitlab-ci.yml", :role => "dialog", :tabindex => "-1"}
-    .modal-dialog
-      .modal-content
-        .modal-header
-          %button.close{"aria-hidden" => "true", "data-dismiss" => "modal", :type => "button"} 脳
-          %h4.modal-title Content of .gitlab-ci.yml
-        .modal-body
-          = text_area_tag :yaml, @ci_project.generated_yaml_config, size: "70x25", class: "form-control"
-        .modal-footer
-          %button.btn.btn-default{"data-dismiss" => "modal", :type => "button"} Close
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 637154f56aa1d00c6562e99a9c1f811312bf940a..9a0e7bff3f1ebc805551909e1fe04cee147fb254 100644
--- a/app/views/projects/commit_statuses/_commit_status.html.haml
+++ b/app/views/projects/commit_statuses/_commit_status.html.haml
@@ -12,14 +12,30 @@
     - if commit_status.show_warning?
       %i.fa.fa-warning.text-warning
 
-  %td
-    = commit_status.ref
+  - 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|
@@ -36,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
@@ -45,10 +61,14 @@
 
   %td
     .pull-right
+      - if current_user && can?(current_user, :download_build_artifacts, @project) && commit_status.download_url
+        = link_to commit_status.download_url, title: 'Download artifacts' do
+          %i.fa.fa-download
       - 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/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder
index 3854ad5d6116890bacfdfcd67ecb2e870acc73f0..268b9b815ee89b8197abec3a523df41c7c6f6619 100644
--- a/app/views/projects/commits/show.atom.builder
+++ b/app/views/projects/commits/show.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
       xml.link    href: namespace_project_commit_url(@project.namespace, @project, id: commit.id)
       xml.title   truncate(commit.title, length: 80)
       xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")
-      xml.media   :thumbnail, width: "40", height: "40", url: avatar_icon(commit.author_email)
+      xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email))
       xml.author do |author|
         xml.name commit.author_name
         xml.email commit.author_email
diff --git a/app/views/projects/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..3ebc175648ee985eadf314f9f16edcc41d29541b 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -57,7 +57,16 @@
                     = f.check_box :merge_requests_enabled
                     %strong Merge Requests
                     %br
-                    %span.descr Submit changes to be merged upstream.
+                    %span.descr Submit changes to be merged upstream
+
+            .form-group
+              .col-sm-offset-2.col-sm-10
+                .checkbox
+                  = f.label :builds_enabled do
+                    = f.check_box :builds_enabled
+                    %strong Builds
+                    %br
+                    %span.descr Test and deploy your changes before merge
 
             .form-group
               .col-sm-offset-2.col-sm-10
@@ -189,6 +198,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 +225,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..03d0733f913268357e05805f9fc0ebba6b76aef7 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -1,9 +1,9 @@
-%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
     = link_to 'Commits', commits_namespace_project_graph_path
-  - if @project.gitlab_ci?
+  - if @project.builds_enabled?
     = nav_link(action: :ci) do
       = link_to ci_namespace_project_graph_path do
         Continuous Integration
diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml
index 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/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml
index 626970f39be61a083b87ef44a7522bef57255e72..d9cfc3d7ae943ba9df5f8afa7140a76e1463ec17 100644
--- a/app/views/projects/merge_requests/show/_diffs.html.haml
+++ b/app/views/projects/merge_requests/show/_diffs.html.haml
@@ -1,5 +1,5 @@
 - if @merge_request_diff.collected?
-  = render "projects/diffs/diffs", diffs: @merge_request.diffs, project: @merge_request.project
+  = render "projects/diffs/diffs", diffs: params[:w] == '1' ? @merge_request.diffs_no_whitespace : @merge_request.diffs, project: @merge_request.project
 - elsif @merge_request_diff.empty?
   .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch}
 - else
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/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 255ddab479f4b3765eb37d15930540c8e35d2c20..24879b19d2bfc7a3aa7bdf46919da0d8f9cf110e 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_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: :description, classes: 'description form-control js-quick-submit'
-            .hint
-              .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
-              .pull-left Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+            = render 'projects/notes/hints'
           .clearfix
           .error-alert
     .col-md-6
@@ -45,7 +43,7 @@
 
 
 :javascript
-  $( ".datepicker" ).datepicker({
+  $(".datepicker").datepicker({
     dateFormat: "yy-mm-dd",
     onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
   }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
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 5d184730796f9112b6f4db44967b0aa934a089ba..8880830198514bb28276955e60f614b12b52f007 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -2,7 +2,7 @@
   .timeline-entry-inner
     .timeline-icon
       %a{href: user_path(note.author)}
-        %img.avatar.s40{src: avatar_icon(note.author), alt: ''}
+        = image_tag avatar_icon(note.author), alt: '', class: 'avatar s40'
     .timeline-content
       .note-header
         - if note_editable?(note)
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 2a64708d07c35b021b779fd0d89656e297e29eb4..d5a92cb816adf83ea7e507a43a91ca97b41c855d 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, 400), target: '_blank' do
-        = image_tag avatar_icon(@user, 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 "https://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do
+          = icon('linkedin-square')
+    - unless @user.twitter.blank?
+      .profile-link-holder
+        = link_to "https://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/test.rb b/config/environments/test.rb
index 2d5e7addcd3ebe1b6d3dc5661e962c6790415370..955540837d31dccf6bf122a217b54ca086e1eaed 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -30,4 +30,6 @@ Gitlab::Application.configure do
   config.active_support.deprecation = :stderr
 
   config.eager_load = false
+
+  config.cache_store = :null_store
 end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 8b85981497adecbb42f56c4277f92297f10fc3ea..8fdb2603ce8f1d86eb299814beb37028721c0d3f 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -84,6 +84,7 @@ production: &base
       merge_requests: true
       wiki: true
       snippets: false
+      builds: true
 
     ## Webhook settings
     # Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10)
@@ -123,6 +124,12 @@ production: &base
     # The mailbox where incoming mail will end up. Usually "inbox".
     mailbox: "inbox"
 
+  ## Git LFS
+  lfs:
+    enabled: false
+    # The location where LFS objects are stored (default: shared/lfs-objects).
+    # storage_path: shared/lfs-objects
+
   ## Gravatar
   ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
   gravatar:
@@ -274,27 +281,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: {
@@ -310,7 +318,9 @@ production: &base
       #       application_name: 'YOUR_APP_NAME',
       #       application_password: 'YOUR_APP_PASSWORD' } }
 
-
+  # Shared file storage settings
+  shared:
+    # path: /mnt/gitlab # Default: shared
 
 
   #
@@ -318,10 +328,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:
@@ -411,6 +423,8 @@ test:
   <<: *base
   gravatar:
     enabled: true
+  lfs:
+    enabled: false
   gitlab:
     host: localhost
     port: 80
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index d5493ca038d0399671bdffbf62f8c6bb600b010c..6b7990c0ab06f8f703bf5e57de35ca18bf6cc9c9 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']  ||= {}
 
 #
@@ -168,8 +171,9 @@ Settings.gitlab.default_projects_features['issues']         = true if Settings.g
 Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
 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['builds']         = true if Settings.gitlab.default_projects_features['builds'].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,10 +182,12 @@ 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)
+Settings.gitlab_ci['max_artifacts_size']    ||= 100 # in megabytes
 
 #
 # Reply by email
@@ -189,10 +195,17 @@ Settings.gitlab_ci['builds_path']         = File.expand_path(Settings.gitlab_ci[
 Settings['incoming_email'] ||= Settingslogic.new({})
 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']        = 143 if Settings.incoming_email['ssl'].nil?
-Settings.incoming_email['start_tls']  = 143 if Settings.incoming_email['start_tls'].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?
 
+#
+# Git LFS
+#
+Settings['lfs'] ||= Settingslogic.new({})
+Settings.lfs['enabled']      = false if Settings.lfs['enabled'].nil?
+Settings.lfs['storage_path'] = File.expand_path(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects"), Rails.root)
+
 #
 # Gravatar
 #
@@ -242,9 +255,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/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_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/session_store.rb b/config/initializers/session_store.rb
index 04ed9e90df5300329d980acd0d6d1f7d1736e369..d7c5432da76553e310ab3ae7dcdc9ac827a8c425 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -9,12 +9,14 @@ begin
 rescue
 end
 
-Gitlab::Application.config.session_store(
-  :redis_store, # Using the cookie_store would enable session replay attacks.
-  servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store
-  key: '_gitlab_session',
-  secure: Gitlab.config.gitlab.https,
-  httponly: true,
-  expire_after: Settings.gitlab['session_expire_delay'] * 60,
-  path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root
-)
+unless Rails.env.test?
+  Gitlab::Application.config.session_store(
+    :redis_store, # Using the cookie_store would enable session replay attacks.
+    servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store
+    key: '_gitlab_session',
+    secure: Gitlab.config.gitlab.https,
+    httponly: true,
+    expire_after: Settings.gitlab['session_expire_delay'] * 60,
+    path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root
+  )
+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/routes.rb b/config/routes.rb
index 3dbe2c4dfcceead34c2aa8d2b6113cfcf0554940..0bc2c17345337bdec28ba6422dc54dba881d8dd4 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
@@ -19,12 +32,9 @@ Gitlab::Application.routes.draw do
         get :status, to: 'projects#badge'
         get :integration
         post :toggle_shared_runners
-        get :dumped_yaml
       end
 
       resources :runner_projects, only: [:create, :destroy]
-
-      resources :events, only: [:index]
     end
 
     resource :user_sessions do
@@ -83,7 +93,7 @@ Gitlab::Application.routes.draw do
   end
 
   # Enable Grack support
-  mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post]
+  mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put]
 
   # Help
   get 'help'                  => 'help#index'
@@ -212,6 +222,8 @@ Gitlab::Application.routes.draw do
       resources :keys, only: [:show, :destroy]
       resources :identities, only: [:index, :edit, :update, :destroy]
 
+      delete 'stop_impersonation' => 'impersonation#destroy', on: :collection
+
       member do
         get :projects
         get :keys
@@ -221,7 +233,7 @@ Gitlab::Application.routes.draw do
         put :unblock
         put :unlock
         put :confirm
-        post :login_as
+        post 'impersonate' => 'impersonation#create'
         patch :disable_two_factor
         delete 'remove/:email_id', action: 'remove_email', as: 'remove_email'
       end
@@ -356,7 +368,7 @@ Gitlab::Application.routes.draw do
       end
 
       resource :avatar, only: [:destroy]
-      resources :milestones, only: [:index, :show, :update]
+      resources :milestones, only: [:index, :show, :update, :new, :create]
     end
   end
 
@@ -378,6 +390,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 +486,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
 
@@ -570,7 +584,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]
@@ -589,12 +606,13 @@ Gitlab::Application.routes.draw do
 
         resources :builds, only: [:index, :show] do
           collection do
-            get :cancel_all
+            post :cancel_all
           end
 
           member do
-            get :cancel
             get :status
+            post :cancel
+            get :download
             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/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/20151013092124_add_artifacts_file_to_builds.rb b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5a299f7b26dd011909c83d5aea552f6bc1460d74
--- /dev/null
+++ b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
@@ -0,0 +1,5 @@
+class AddArtifactsFileToBuilds < ActiveRecord::Migration
+  def change
+    add_column :ci_builds, :artifacts_file, :text
+  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/20151103134857_create_lfs_objects.rb b/db/migrate/20151103134857_create_lfs_objects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2d04c170a88c9f9c0eb4d5bcb2392b5b7d50ff66
--- /dev/null
+++ b/db/migrate/20151103134857_create_lfs_objects.rb
@@ -0,0 +1,10 @@
+class CreateLfsObjects < ActiveRecord::Migration
+  def change
+    create_table :lfs_objects do |t|
+      t.string :oid, null: false, unique: true
+      t.integer :size, null: false
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20151103134958_create_lfs_objects_projects.rb b/db/migrate/20151103134958_create_lfs_objects_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f3f58b931ece32a085ab123006f210e6727fb0b4
--- /dev/null
+++ b/db/migrate/20151103134958_create_lfs_objects_projects.rb
@@ -0,0 +1,12 @@
+class CreateLfsObjectsProjects < ActiveRecord::Migration
+  def change
+    create_table :lfs_objects_projects do |t|
+      t.integer :lfs_object_id, null: false
+      t.integer :project_id, null: false
+
+      t.timestamps
+    end
+
+    add_index :lfs_objects_projects, :project_id
+  end
+end
diff --git a/db/migrate/20151104105513_add_file_to_lfs_objects.rb b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7c57f3f0df655017afcd6ed4acba5af3b415973a
--- /dev/null
+++ b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
@@ -0,0 +1,5 @@
+class AddFileToLfsObjects < ActiveRecord::Migration
+  def change
+    add_column :lfs_objects, :file, :string
+  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/20151109100728_add_max_artifacts_size_to_application_settings.rb b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..01d8c0f043eb7f1c4987056d87b4f64b1581dea3
--- /dev/null
+++ b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
@@ -0,0 +1,5 @@
+class AddMaxArtifactsSizeToApplicationSettings < ActiveRecord::Migration
+  def change
+    add_column :application_settings, :max_artifacts_size, :integer, default: 100, null: false
+  end
+end
diff --git a/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb b/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d10f1f6e6054ab5f5561082fe95b0039695f8aa1
--- /dev/null
+++ b/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb
@@ -0,0 +1,6 @@
+class AddIndexForLfsOidAndSize < ActiveRecord::Migration
+  def change
+    add_index :lfs_objects, :oid
+    add_index :lfs_objects, [:oid, :size]
+  end
+end
diff --git a/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb b/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..41b93da0a8699036002deea43de92c2cb2bf2ad5
--- /dev/null
+++ b/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb
@@ -0,0 +1,7 @@
+class AddUniqueForLfsOidIndex < ActiveRecord::Migration
+  def change
+    remove_index :lfs_objects, :oid
+    remove_index :lfs_objects, [:oid, :size]
+    add_index :lfs_objects, :oid, unique: true
+  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..aa76cef9fe4c2b6b72f3ea718a2872df8a072ab8 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: 20151116144118) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -46,6 +46,9 @@ 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
+    t.integer  "max_artifacts_size",           default: 100,   null: false
   end
 
   create_table "audit_events", force: true do |t|
@@ -106,15 +109,19 @@ ActiveRecord::Schema.define(version: 20151008130321) do
     t.string   "type"
     t.string   "target_url"
     t.string   "description"
+    t.text     "artifacts_file"
   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 +137,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 +197,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
@@ -411,6 +422,25 @@ ActiveRecord::Schema.define(version: 20151008130321) do
 
   add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree
 
+  create_table "lfs_objects", force: true do |t|
+    t.string   "oid",        null: false
+    t.integer  "size",       null: false
+    t.datetime "created_at"
+    t.datetime "updated_at"
+    t.string   "file"
+  end
+
+  add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree
+
+  create_table "lfs_objects_projects", force: true do |t|
+    t.integer  "lfs_object_id", null: false
+    t.integer  "project_id",    null: false
+    t.datetime "created_at"
+    t.datetime "updated_at"
+  end
+
+  add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree
+
   create_table "members", force: true do |t|
     t.integer  "access_level",       null: false
     t.integer  "source_id",          null: false
@@ -493,14 +523,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 +560,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 +647,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 +660,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 +701,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..93d62b751e66ab0743e7ab5a0fa3b324952f371d 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
   }
 ]
 ```
@@ -186,7 +188,7 @@ Parameters:
     "target_url": "http://jenkins/project/url",
     "description": "Jenkins success",
     "created_at": "2015-10-12T09:47:16.250Z",
-    "started_at": "2015-10-12T09:47:16.250Z"",
+    "started_at": "2015-10-12T09:47:16.250Z",
     "finished_at": "2015-10-12T09:47:16.262Z",
     "author": {
       "id": 1,
@@ -226,7 +228,7 @@ POST /projects/:id/statuses/:sha
   "target_url": "http://jenkins/project/url",
   "description": "Jenkins success",
   "created_at": "2015-10-12T09:47:16.250Z",
-  "started_at": "2015-10-12T09:47:16.250Z"",
+  "started_at": "2015-10-12T09:47:16.250Z",
   "finished_at": "2015-10-12T09:47:16.262Z",
   "author": {
     "id": 1,
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 9648585703530f6be3923a651ec7ddd4a9b5912b..755cc6525c2d2fea7e84422b06c9ecfbe7b2ad85 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -60,6 +60,7 @@ Parameters:
     "path_with_namespace": "diaspora/diaspora-client",
     "issues_enabled": true,
     "merge_requests_enabled": true,
+    "builds_enabled": true,
     "wiki_enabled": true,
     "snippets_enabled": false,
     "created_at": "2013-09-30T13: 46: 02Z",
@@ -101,6 +102,7 @@ Parameters:
     "path_with_namespace": "brightbox/puppet",
     "issues_enabled": true,
     "merge_requests_enabled": true,
+    "builds_enabled": true,
     "wiki_enabled": true,
     "snippets_enabled": false,
     "created_at": "2013-09-30T13:46:02Z",
@@ -191,6 +193,7 @@ Parameters:
   "path_with_namespace": "diaspora/diaspora-project-site",
   "issues_enabled": true,
   "merge_requests_enabled": true,
+  "builds_enabled": true,
   "wiki_enabled": true,
   "snippets_enabled": false,
   "created_at": "2013-09-30T13: 46: 02Z",
@@ -312,6 +315,7 @@ Parameters:
 - `description` (optional) - short project description
 - `issues_enabled` (optional)
 - `merge_requests_enabled` (optional)
+- `builds_enabled` (optional)
 - `wiki_enabled` (optional)
 - `snippets_enabled` (optional)
 - `public` (optional) - if `true` same as setting visibility_level = 20
@@ -334,6 +338,7 @@ Parameters:
 - `default_branch` (optional) - 'master' by default
 - `issues_enabled` (optional)
 - `merge_requests_enabled` (optional)
+- `builds_enabled` (optional)
 - `wiki_enabled` (optional)
 - `snippets_enabled` (optional)
 - `public` (optional) - if `true` same as setting visibility_level = 20
@@ -357,6 +362,7 @@ Parameters:
 - `default_branch` (optional)
 - `issues_enabled` (optional)
 - `merge_requests_enabled` (optional)
+- `builds_enabled` (optional)
 - `wiki_enabled` (optional)
 - `snippets_enabled` (optional)
 - `public` (optional) - if `true` same as setting visibility_level = 20
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 331674538028b4d4cbc5d85dad52ccce25fad98f..b6cca5d4e2ad6892a00c8bc11503d8738a68fa8d 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -1,79 +1,5 @@
 # Repositories
 
-## List project repository tags
-
-Get a list of repository tags from a project, sorted by name in reverse alphabetical order.
-
-```
-GET /projects/:id/repository/tags
-```
-
-Parameters:
-
-- `id` (required) - The ID of a project
-
-```json
-[
-  {
-    "commit": {
-      "author_name": "John Smith",
-      "author_email": "john@example.com",
-      "authored_date": "2012-05-28T04:42:42-07:00",
-      "committed_date": "2012-05-28T04:42:42-07:00",
-      "committer_name": "Jack Smith",
-      "committer_email": "jack@example.com",
-      "id": "2695effb5807a22ff3d138d593fd856244e155e7",
-      "message": "Initial commit",
-      "parents_ids": [
-        "2a4b78934375d7f53875269ffd4f45fd83a84ebe"
-      ]
-    },
-    "name": "v1.0.0",
-    "message": null
-  }
-]
-```
-
-## Create a new tag
-
-Creates new tag in the repository that points to the supplied ref.
-
-```
-POST /projects/:id/repository/tags
-```
-
-Parameters:
-
-- `id` (required) - The ID of a project
-- `tag_name` (required) - The name of a tag
-- `ref` (required) - Create tag using commit SHA, another tag name, or branch name.
-- `message` (optional) - Creates annotated tag.
-
-```json
-{
-  "commit": {
-    "author_name": "John Smith",
-    "author_email": "john@example.com",
-    "authored_date": "2012-05-28T04:42:42-07:00",
-    "committed_date": "2012-05-28T04:42:42-07:00",
-    "committer_name": "Jack Smith",
-    "committer_email": "jack@example.com",
-    "id": "2695effb5807a22ff3d138d593fd856244e155e7",
-    "message": "Initial commit",
-    "parents_ids": [
-      "2a4b78934375d7f53875269ffd4f45fd83a84ebe"
-    ]
-  },
-  "name": "v1.0.0",
-  "message": null
-}
-```
-The message will be `nil` when creating a lightweight tag otherwise
-it will contain the annotation.
-
-It returns 200 if the operation succeed. In case of an error,
-405 with an explaining error message is returned.
-
 ## List repository tree
 
 Get a list of repository files and directories in a project.
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/api/tags.md b/doc/api/tags.md
new file mode 100644
index 0000000000000000000000000000000000000000..b5b90cf6b82881fba013b30a9a7a700e9abc7aa6
--- /dev/null
+++ b/doc/api/tags.md
@@ -0,0 +1,106 @@
+# Tags
+
+## List project repository tags
+
+Get a list of repository tags from a project, sorted by name in reverse alphabetical order.
+
+```
+GET /projects/:id/repository/tags
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+
+```json
+[
+  {
+    "commit": {
+      "author_name": "John Smith",
+      "author_email": "john@example.com",
+      "authored_date": "2012-05-28T04:42:42-07:00",
+      "committed_date": "2012-05-28T04:42:42-07:00",
+      "committer_name": "Jack Smith",
+      "committer_email": "jack@example.com",
+      "id": "2695effb5807a22ff3d138d593fd856244e155e7",
+      "message": "Initial commit",
+      "parents_ids": [
+        "2a4b78934375d7f53875269ffd4f45fd83a84ebe"
+      ]
+    },
+    "release": {
+      "tag": "1.0.0",
+      "description": "Amazing release. Wow"
+    },
+    "name": "v1.0.0",
+    "message": null
+  }
+]
+```
+
+## Create a new tag
+
+Creates a new tag in the repository that points to the supplied ref.
+
+```
+POST /projects/:id/repository/tags
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `tag_name` (required) - The name of a tag
+- `ref` (required) - Create tag using commit SHA, another tag name, or branch name.
+- `message` (optional) - Creates annotated tag.
+- `release_description` (optional) - Add release notes to the git tag and store it in the GitLab database.
+
+```json
+{
+  "commit": {
+    "author_name": "John Smith",
+    "author_email": "john@example.com",
+    "authored_date": "2012-05-28T04:42:42-07:00",
+    "committed_date": "2012-05-28T04:42:42-07:00",
+    "committer_name": "Jack Smith",
+    "committer_email": "jack@example.com",
+    "id": "2695effb5807a22ff3d138d593fd856244e155e7",
+    "message": "Initial commit",
+    "parents_ids": [
+      "2a4b78934375d7f53875269ffd4f45fd83a84ebe"
+    ]
+  },
+  "release": {
+    "tag": "1.0.0",
+    "description": "Amazing release. Wow"
+  },
+  "name": "v1.0.0",
+  "message": null
+}
+```
+The message will be `nil` when creating a lightweight tag otherwise
+it will contain the annotation.
+
+It returns 200 if the operation succeed. In case of an error,
+405 with an explaining error message is returned.
+
+
+## New release
+
+Add release notes to the existing git tag
+
+```
+PUT /projects/:id/repository/:tag/release
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `tag` (required) - The name of a tag
+- `description` (required) - Release notes with markdown support
+
+```json
+{
+  "tag": "1.0.0",
+  "description": "Amazing release. Wow"
+}
+```
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_build.md b/doc/ci/docker/using_docker_build.md
index 5af27470d2fc31106bfea08f0afb64fa1ad8e101..4b1788a9af0c89bda6aa331c3e95a1f01adde818 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -35,7 +35,7 @@ GitLab Runner then executes build scripts as `gitlab-runner` user.
 
     ```bash
     $ sudo gitlab-runner register -n \
-      --url http://gitlab.com/ci \
+      --url https://gitlab.com/ci \
       --token RUNNER_TOKEN \
       --executor shell
       --description "My Runner"
@@ -84,7 +84,7 @@ In order to do that follow the steps:
 
     ```bash
     $ sudo gitlab-runner register -n \
-      --url http://gitlab.com/ci \
+      --url https://gitlab.com/ci \
       --token RUNNER_TOKEN \
       --executor docker \
       --description "My Docker Runner" \
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/quick_start/README.md b/doc/ci/quick_start/README.md
index a87a1f806fc226adeb270fb7be27897414993d13..d69064a91fd0cf630533a95974790f170c9cc001 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -6,7 +6,7 @@ To start building projects with GitLab CI a few steps needs to be done.
 
 First you need to have a working GitLab and GitLab CI instance.
 
-You can omit this step if you use [GitLab.com](http://GitLab.com/).
+You can omit this step if you use [GitLab.com](https://GitLab.com/).
 
 ## 2. Create repository on GitLab
 
@@ -16,7 +16,7 @@ Push your application to that repository.
 ## 3. Add project to CI
 
 The next part is to login to GitLab CI.
-Point your browser to the URL you have set GitLab or use [gitlab.com/ci](http://gitlab.com/ci/).
+Point your browser to the URL you have set GitLab or use [gitlab.com/ci](https://gitlab.com/ci/).
 
 On the first screen you will see a list of GitLab's projects that you have access to:
 
@@ -97,7 +97,7 @@ If you do it correctly your runner should be shown under **Runners activated for
 
 ### Shared runners
 
-If you use [gitlab.com/ci](http://gitlab.com/ci/) you can use **Shared runners** provided by GitLab Inc.
+If you use [gitlab.com/ci](https://gitlab.com/ci/) you can use **Shared runners** provided by GitLab Inc.
 These are special virtual machines that are run on GitLab's infrastructure that can build any project.
 To enable **Shared runners** you have to go to **Runners** and click **Enable shared runners** for this project.
 
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 4caeccacb7f94d1414aaecd6a53f658acfa61de9..3e6071a2ee7f17496767b858f7a377ca645f495c 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -56,6 +56,7 @@ There are a few `keywords` that can't be used as job names:
 | types         | optional | Alias for `stages` |
 | before_script | optional | Define commands prepended for each job's script |
 | variables     | optional | Define build variables |
+| cache         | optional | Define list of files that should be cached between subsequent runs |
 
 ### image and services
 This allows to specify a custom Docker image and a list of services that can be used for time of the build.
@@ -110,6 +111,19 @@ These variables can be later used in all executed commands and scripts.
 
 The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them.
 
+### cache
+`cache` is used to specify list of files and directories which should be cached between builds.
+
+**The global setting allows to specify default cached files for all jobs.**
+
+To cache all git untracked files and files in `binaries`:
+```
+cache:
+  untracked: true
+  paths:
+  - binaries/
+```
+
 ## Jobs
 `.gitlab-ci.yml` allows you to specify an unlimited number of jobs.
 Each job has to have a unique `job_name`, which is not one of the keywords mentioned above.
@@ -140,6 +154,9 @@ 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` |
+| artifacts     | optional | Define list build artifacts |
+| cache         | optional | Define list of files that should be cached between subsequent runs |
 
 ### script
 `script` is a shell script which is executed by runner. The shell script is prepended with `before_script`.
@@ -168,7 +185,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 +198,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 +225,138 @@ 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.
+
+### artifacts
+`artifacts` is used to specify list of files and directories which should be attached to build after success.
+
+1. Send all files in `binaries` and `.config`:
+```
+artifacts:
+  paths:
+  - binaries/
+  - .config
+```
+
+2. Send all git untracked files:
+```
+artifacts:
+  untracked: true
+```
+
+3. Send all git untracked files and files in `binaries`:
+```
+artifacts:
+  untracked: true
+  paths:
+  - binaries/
+```
+
+The artifacts will be send after the build success to GitLab and will be accessible in GitLab interface to download.
+
+This feature requires GitLab Runner v0.7.0 or higher.
+
+### cache
+`cache` is used to specify list of files and directories which should be cached between builds.
+
+1. Cache all files in `binaries` and `.config`:
+```
+rspec:
+  script: test
+  cache:
+    paths:
+    - binaries/
+    - .config
+```
+
+2. Cache all git untracked files:
+```
+rspec:
+  script: test
+  cache:
+    untracked: true
+```
+
+3. Cache all git untracked files and files in `binaries`:
+```
+rspec:
+  script: test
+  cache:
+    untracked: true
+    paths:
+    - binaries/
+```
+
+4. Locally defined cache overwrites globally defined options. This will cache only `binaries/`:
+
+```
+cache:
+  paths:
+  - my/files
+
+rspec:
+  script: test
+  cache:
+    paths:
+    - binaries/
+```
+
+The cache is provided on best effort basis, so don't expect that cache will be present.
+For implementation details please check GitLab Runner.
+
+This feature requires GitLab Runner v0.7.0 or higher.
+
+
 ## 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/customization/libravatar.md b/doc/customization/libravatar.md
index 54c1780c3abeef85a906e93dfbcb063de2f34ce4..bd2c242afc2bf031fdbb7a0d779f2bf8ef13f818 100644
--- a/doc/customization/libravatar.md
+++ b/doc/customization/libravatar.md
@@ -2,7 +2,7 @@
 
 GitLab by default supports [Gravatar](https://gravatar.com) avatar service.
 Libravatar is a service which delivers your avatar (profile picture) to other websites and their API is
-[heavily based on gravatar](http://wiki.libravatar.org/api/).
+[heavily based on gravatar](https://wiki.libravatar.org/api/).
 
 This means that it is not complicated to switch to Libravatar avatar service or even self hosted Libravatar server.
 
@@ -31,7 +31,7 @@ the configuration options as follows:
 
 ## Self-hosted
 
-If you are [running your own libravatar service](http://wiki.libravatar.org/running_your_own/) the URL will be different in the configuration
+If you are [running your own libravatar service](https://wiki.libravatar.org/running_your_own/) the URL will be different in the configuration
 but the important part is to provide the same placeholders so GitLab can parse the URL correctly.
 
 For example, you host a service on `http://libravatar.example.com` the `plain_url` you need to supply in `gitlab.yml` is
@@ -63,7 +63,7 @@ Run `sudo gitlab-ctl reconfigure` for changes to take effect.
 
 ## Default URL for missing images
 
-[Libravatar supports different sets](http://wiki.libravatar.org/api/) of `missing images` for emails not found on the Libravatar service.
+[Libravatar supports different sets](https://wiki.libravatar.org/api/) of `missing images` for emails not found on the Libravatar service.
 
 In order to use a different set other than `identicon`, replace `&d=identicon` portion of the URL with another supported set.
 For example, you can use `retro` set in which case the URL would look like: `plain_url: "http://cdn.libravatar.org/avatar/%{hash}?s=%{size}&d=retro"`
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index c00d290371e032e45beea68e51236ea00f54db55..6101a71a8dea813add6189ae68b90a8c74fcfa7e 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -146,7 +146,7 @@ nginx
 
 Apache httpd
 
-- [Explanation of Apache logs](http://httpd.apache.org/docs/2.2/logs.html).
+- [Explanation of Apache logs](https://httpd.apache.org/docs/2.2/logs.html).
 - `/var/log/apache2/` contains error and output logs (on Ubuntu).
 - `/var/log/httpd/` contains error and output logs (on RHEL).
 
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index 80c86ef921e9fed698feda43b3c1fa26bc4cfb0a..e244ad4e881d6caae77437e8f0d515f81a33f8cc 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -4,11 +4,15 @@ 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.
 
-## rack-mini-profiler
+## Sherlock
 
-This Gem is enabled by default in development only. It allows you to see the
-timings of the various components that made up a web request (e.g. the SQL
-queries executed and their execution timings).
+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
 
@@ -21,36 +25,3 @@ starting GitLab. For example:
 
 Bullet will log query problems to both the Rails log as well as the Chrome
 console.
-
-## ActiveRecord Query Trace
-
-This Gem adds backtraces for every ActiveRecord query in the Rails console. This
-can be useful to track down where a query was executed. Because this Gem adds
-quite a bit of noise (5-10 extra lines per ActiveRecord query) it's disabled by
-default. To use this Gem you'll need to set `ENABLE_QUERY_TRACE` to a non empty
-file before starting GitLab. For example:
-
-    ENABLE_QUERY_TRACE=true bundle exec rails s
-
-## rack-lineprof
-
-This is a Gem that can trace the execution time of code on a per line basis.
-Because this Gem can add quite a bit of overhead it's disabled by default. To
-enable it, set the environment variable `ENABLE_LINEPROF` to a non-empty value.
-For example:
-
-    ENABLE_LINEPROF=true bundle exec rails s
-
-Once enabled you'll need to add a query string parameter to a request to
-actually profile code execution. The name of the parameter is `lineprof` and
-should be set to a regular expression (minus the starting/ending slash) used to
-select what files to profile. To profile all files containing "foo" somewhere in
-the path you'd use the following parameter:
-
-    ?lineprof=foo
-
-Or when filtering for files containing "foo" and "bar" in their path:
-
-    ?lineprof=foo|bar
-
-Once set the profiling output will be displayed in your terminal.
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/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md
index 548c484bc0842e7edde8ac0750e971a140bec21e..0f2665a3bf7162a4cf4b216296a9f6e6041f676e 100644
--- a/doc/hooks/custom_hooks.md
+++ b/doc/hooks/custom_hooks.md
@@ -7,7 +7,7 @@ Please explore webhooks as an option if you do not have filesystem access. For a
 Git natively supports hooks that are executed on different actions.
 Examples of server-side git hooks include pre-receive, post-receive, and update.
 See
-[Git SCM Server-Side Hooks](http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks)
+[Git SCM Server-Side Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks)
 for more information about each hook type.
 
 As of gitlab-shell version 2.2.0 (which requires GitLab 7.5+), GitLab
diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md
index c565e90da2f505a140cd90787c5690229087a18c..513ad69ec26d9f07ae5656e2d0955c632fc48622 100644
--- a/doc/install/database_mysql.md
+++ b/doc/install/database_mysql.md
@@ -2,7 +2,7 @@
 
 ## Note
 
-We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](http://bugs.mysql.com/bug.php?id=65830) that [suggested](http://bugs.mysql.com/bug.php?id=50909) [fixes](http://bugs.mysql.com/bug.php?id=65830) [have](http://bugs.mysql.com/bug.php?id=63164).
+We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](https://bugs.mysql.com/bug.php?id=65830) that [suggested](https://bugs.mysql.com/bug.php?id=50909) [fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164).
 
 ## MySQL
 
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 3c62b11988e240d029d631fb4fbc5bb319106746..7ef46b0406539d674944ba7596005ac7042a9cf3 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -106,7 +106,7 @@ Then select 'Internet Site' and press enter to confirm the hostname.
 
 ## 2. Ruby
 
-The use of Ruby version managers such as [RVM](http://rvm.io/), [rbenv](https://github.com/sstephenson/rbenv) or [chruby](https://github.com/postmodern/chruby) with GitLab in production frequently leads to hard to diagnose problems. For example, GitLab Shell is called from OpenSSH and having a version manager can prevent pushing and pulling over SSH. Version managers are not supported and we strongly advise everyone to follow the instructions below to use a system Ruby.
+The use of Ruby version managers such as [RVM](https://rvm.io/), [rbenv](https://github.com/sstephenson/rbenv) or [chruby](https://github.com/postmodern/chruby) with GitLab in production frequently leads to hard to diagnose problems. For example, GitLab Shell is called from OpenSSH and having a version manager can prevent pushing and pulling over SSH. Version managers are not supported and we strongly advise everyone to follow the instructions below to use a system Ruby.
 
 Remove the old Ruby 1.8 if present
 
@@ -128,11 +128,10 @@ Install the Bundler Gem:
 
 ## 3. Go
 
-Since GitLab 8.0, Git HTTP requests are handled by gitlab-git-http-server.
-This is a small daemon written in Go.
-To install gitlab-git-http-server we need a Go compiler.
-The instructions below assume you use 64-bit Linux. You can find
-downloads for other platforms at the [Go download
+Since GitLab 8.0, Git HTTP requests are handled by gitlab-workhorse (formerly
+gitlab-git-http-server). This is a small daemon written in Go. To install
+gitlab-workhorse we need a Go compiler. The instructions below assume you
+use 64-bit Linux. You can find downloads for other platforms at the [Go download
 page](https://golang.org/dl).
 
     curl -O --progress https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz
@@ -211,9 +210,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-2-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-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
 
 ### Configure It
 
@@ -246,6 +245,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
     # Change the permissions of the directory where CI build traces are stored
     sudo chmod -R u+rwX builds/
 
+    # Change the permissions of the directory where CI artifacts are stored
+    sudo chmod -R u+rwX shared/artifacts/
+
     # Copy the example Unicorn config
     sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
 
@@ -253,8 +255,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
@@ -295,7 +297,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
 
 ### Install Gems
 
-**Note:** As of bundler 1.5.2, you can invoke `bundle install -jN` (where `N` the number of your processor cores) and enjoy the parallel gems installation with measurable difference in completion time (~60% faster). Check the number of your cores with `nproc`. For more information check this [post](http://robots.thoughtbot.com/parallel-gem-installing-using-bundler). First make sure you have bundler >= 1.5.2 (run `bundle -v`) as it addresses some [issues](https://devcenter.heroku.com/changelog-items/411) that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2.
+**Note:** As of bundler 1.5.2, you can invoke `bundle install -jN` (where `N` the number of your processor cores) and enjoy the parallel gems installation with measurable difference in completion time (~60% faster). Check the number of your cores with `nproc`. For more information check this [post](https://robots.thoughtbot.com/parallel-gem-installing-using-bundler). First make sure you have bundler >= 1.5.2 (run `bundle -v`) as it addresses some [issues](https://devcenter.heroku.com/changelog-items/411) that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2.
 
     # For PostgreSQL (note, the option says "without ... mysql")
     sudo -u git -H bundle install --deployment --without development test mysql aws kerberos
@@ -310,7 +312,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:
@@ -320,15 +322,20 @@ GitLab Shell is an SSH access and repository management software developed speci
 
 **Note:** Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in /etc/hosts ("127.0.0.1  hostname"). This might be necessary for example if you set up gitlab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with "Check GitLab API access: FAILED. code: 401" and pushing commits will be rejected with "[remote rejected] master -> master (hook declined)".
 
-### Install gitlab-git-http-server
+### Install gitlab-workhorse
 
     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 clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
+    cd gitlab-workhorse
+    sudo -u git -H git checkout 0.4.1
     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
 
     # Type 'yes' to create the database tables.
@@ -345,11 +352,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 +495,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/ldap.md b/doc/integration/ldap.md
index 9b7d8fa3969694931a22505468d1ea89d8f546d2..7e2920b8865e6aeb7d4dfed2af12cb67640e5360 100644
--- a/doc/integration/ldap.md
+++ b/doc/integration/ldap.md
@@ -71,7 +71,7 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
 
   # Filter LDAP users
   #
-  #   Format: RFC 4515 http://tools.ietf.org/search/rfc4515
+  #   Format: RFC 4515 https://tools.ietf.org/search/rfc4515
   #   Ex. (employeeType=developer)
   #
   #   Note: GitLab does not support omniauth-ldap's custom filter syntax.
@@ -145,7 +145,7 @@ If multiple LDAP email attributes are present, e.g. `mail: foo@bar.com` and `ema
 ## Using an LDAP filter to limit access to your GitLab server
 
 If you want to limit all GitLab access to a subset of the LDAP users on your LDAP server you can set up an LDAP user filter.
-The filter must comply with [RFC 4515](http://tools.ietf.org/search/rfc4515).
+The filter must comply with [RFC 4515](https://tools.ietf.org/search/rfc4515).
 
 ```ruby
 # For omnibus packages; new LDAP server syntax
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index c5cecbc2f2dc50e138ad7d15dab0b1b82f93b39d..f2b1721fc0304f38007df293d67c363d04702ed4 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -36,7 +36,7 @@ If you want to change these settings:
     ```
     gitlab_rails['omniauth_enabled'] = true
     gitlab_rails['omniauth_allow_single_sign_on'] = false
-    gitlab_rails['block_auto_created_users'] = true
+    gitlab_rails['omniauth_block_auto_created_users'] = true
     ```
 
 * **For installations from source**
@@ -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/legal/corporate_contributor_license_agreement.md b/doc/legal/corporate_contributor_license_agreement.md
index 13bf15fcf45c0c7b260d022aa0f1f6d6b02e2480..7b94506c29785b55d27a9e2c1ea40fa3ed3779ae 100644
--- a/doc/legal/corporate_contributor_license_agreement.md
+++ b/doc/legal/corporate_contributor_license_agreement.md
@@ -22,4 +22,4 @@ You accept and agree to the following terms and conditions for Your present and
 
 8.  It is your responsibility to notify GitLab B.V. when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with GitLab B.V..
 
-This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
+This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
diff --git a/doc/legal/individual_contributor_license_agreement.md b/doc/legal/individual_contributor_license_agreement.md
index 72b01433dd012433f46966ebc679f551283daafd..f97c252fd7cfa11bb270ee6b4ce0a3994e11b8f1 100644
--- a/doc/legal/individual_contributor_license_agreement.md
+++ b/doc/legal/individual_contributor_license_agreement.md
@@ -22,4 +22,4 @@ You accept and agree to the following terms and conditions for Your present and
 
 8.  You agree to notify GitLab B.V. of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
 
-This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
+This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index ac3851f8c95fa9a1acc8ea95fa244382422a6844..bc8e7d155e7f761ff296f8617fd1b5e1457e4ec1 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -43,7 +43,7 @@ You can also use other rich text files in GitLab. You might have to install a de
 
 ## Newlines
 
-GFM honors the markdown specification in how [paragraphs and line breaks are handled](http://daringfireball.net/projects/markdown/syntax#p).
+GFM honors the markdown specification in how [paragraphs and line breaks are handled](https://daringfireball.net/projects/markdown/syntax#p).
 
 A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.  
 Line-breaks, or softreturns, are rendered if you end a line with two or more spaces
@@ -72,14 +72,14 @@ do_this_and_do_that_and_another_thing
 
 GFM will autolink almost any URL you copy and paste into your text.
 
-    * http://www.google.com
+    * https://www.google.com
     * https://google.com/
     * ftp://ftp.us.debian.org/debian/
     * smb://foo/bar/baz
     * irc://irc.freenode.net/gitlab
     * http://localhost:3000
 
-* http://www.google.com
+* https://www.google.com
 * https://google.com/
 * ftp://ftp.us.debian.org/debian/
 * smb://foo/bar/baz
@@ -390,7 +390,7 @@ There are two ways to create links, inline-style and reference-style.
 
     [arbitrary case-insensitive reference text]: https://www.mozilla.org
     [1]: http://slashdot.org
-    [link text itself]: http://www.reddit.com
+    [link text itself]: https://www.reddit.com
 
 [I'm an inline-style link](https://www.google.com)
 
@@ -406,7 +406,7 @@ Some text to show that the reference links can follow later.
 
 [arbitrary case-insensitive reference text]: https://www.mozilla.org
 [1]: http://slashdot.org
-[link text itself]: http://www.reddit.com
+[link text itself]: https://www.reddit.com
 
 **Note**
 
@@ -583,5 +583,5 @@ By including colons in the header row, you can align the text within that column
 ## References
 
 - This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
-- The [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
+- The [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
 - [Dillinger.io](http://dillinger.io) is a handy tool for testing standard markdown.
diff --git a/doc/operations/unicorn.md b/doc/operations/unicorn.md
index 31b432cd411df24d2308d085d4ef6873bbf05e6a..bad61151bda1ff45f7fb7a57a7f18026047f7b83 100644
--- a/doc/operations/unicorn.md
+++ b/doc/operations/unicorn.md
@@ -52,7 +52,7 @@ leak memory, probably because it does not handle user requests.)
 
 To make these memory leaks manageable, GitLab comes with the
 [unicorn-worker-killer gem](https://github.com/kzk/unicorn-worker-killer). This
-gem [monkey-patches](http://en.wikipedia.org/wiki/Monkey_patch) the Unicorn
+gem [monkey-patches](https://en.wikipedia.org/wiki/Monkey_patch) the Unicorn
 workers to do a memory self-check after every 16 requests. If the memory of the
 Unicorn worker exceeds a pre-set limit then the worker process exits. The
 Unicorn master then automatically replaces the worker process.
@@ -78,9 +78,9 @@ 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
 source of confusion for administrators. Usually they are a [red
-herring](http://en.wikipedia.org/wiki/Red_herring).
+herring](https://en.wikipedia.org/wiki/Red_herring).
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..1a5442cdac71838425065f70724fd469b7020010 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,8 @@ 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), artifacts (CI build artifacts).
+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..d347f58ba0fd6bc31b0533458159e07df424fa59 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)
 - [ ] 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)
+- [ ] Merge CE `master` into EE `master` via merge request (#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/release/security.md b/doc/release/security.md
index 60bcfbb6da5b9f94e680076b268d5d0685225bee..b1a62b333e64f1489986d4873c51c81887bcf54f 100644
--- a/doc/release/security.md
+++ b/doc/release/security.md
@@ -8,7 +8,7 @@ Do a security release when there is a critical issue that needs to be addresses
 
 ## Security vulnerability disclosure
 
-Please report suspected security vulnerabilities in private to <support@gitlab.com>, also see the [disclosure section on the GitLab.com website](http://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
+Please report suspected security vulnerabilities in private to <support@gitlab.com>, also see the [disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
 
 ## Release Procedure
 
@@ -25,7 +25,7 @@ Please report suspected security vulnerabilities in private to <support@gitlab.c
 1. Send tweets about the release from `@gitlabhq`
 1. Send out an email to [the community google mailing list](https://groups.google.com/forum/#!forum/gitlabhq)
 1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number. CVE is only needed for bugs that allow someone to own the server (Remote Code Execution) or access to code of projects they are not a member of.
-1. Add the security researcher to the [Security Researcher Acknowledgments list](http://about.gitlab.com/vulnerability-acknowledgements/)
+1. Add the security researcher to the [Security Researcher Acknowledgments list](https://about.gitlab.com/vulnerability-acknowledgements/)
 1. Thank the security researcher in an email for their cooperation
 1. Update the blog post and the CHANGELOG when we receive the CVE number
 
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index b6b8000af4e75b93603ec38948e8e31f76e26f04..9753504ac8be72b46121aeb010518e073191e46e 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
@@ -78,11 +77,11 @@ Deploy keys can be shared between projects, you just need to add them to each pr
 
 ### Eclipse
 
-How to add your ssh key to Eclipse: http://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration
+How to add your ssh key to Eclipse: https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration
 
 ## 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/6.x-or-7.x-to-7.14.md b/doc/update/6.x-or-7.x-to-7.14.md
index b34fb12da6f618293d0f77ac437ac79462e757c8..4516a1020847b2e915686dcf1626f26b6ca7c1b4 100644
--- a/doc/update/6.x-or-7.x-to-7.14.md
+++ b/doc/update/6.x-or-7.x-to-7.14.md
@@ -47,7 +47,7 @@ Download and compile Ruby:
 
 ```bash
 mkdir /tmp/ruby && cd /tmp/ruby
-curl --progress http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.6.tar.gz | tar xz
+curl --progress https://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.6.tar.gz | tar xz
 cd ruby-2.1.6
 ./configure --disable-install-rdoc
 make
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..73d899f9c2eddcfb7eb852fd2777b2bdabd3c599
--- /dev/null
+++ b/doc/update/8.1-to-8.2.md
@@ -0,0 +1,189 @@
+# 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 at least 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.6
+```
+
+### 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.4.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.1)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.0 to 8.1](8.0-to-8.1.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/README.md b/doc/workflow/README.md
index 5b8d72dfd340f60368827d46844cbf4cc595a2f8..c1a4f64981f8149d28973e52b9fb8fd26f8e3d21 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -13,5 +13,7 @@
 - [Project users](add-user/add-user.md)
 - [Protected branches](protected_branches.md)
 - [Web Editor](web_editor.md)
+- [Releases](releases.md)
+- [Milestones](milestones.md)
 - [Merge Requests](merge_requests.md)
 - ["Work In Progress" Merge Requests](wip_merge_requests.md)
diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md
index f608674faf609ab8c9b5fc87b12bbb746b8e40be..31495bce76e10ef6c755a7015f1ee4d2a937ef02 100644
--- a/doc/workflow/gitlab_flow.md
+++ b/doc/workflow/gitlab_flow.md
@@ -7,7 +7,7 @@ This allows a wide variety of branching strategies and workflows.
 Almost all of these are an improvement over the methods used before git.
 But many organizations end up with a workflow that is not clearly defined, overly complex or not integrated with issue tracking systems.
 Therefore we propose the GitLab flow as clearly defined set of best practices.
-It combines [feature driven development](http://en.wikipedia.org/wiki/Feature-driven_development) and [feature branches](http://martinfowler.com/bliki/FeatureBranch.html) with issue tracking.
+It combines [feature driven development](https://en.wikipedia.org/wiki/Feature-driven_development) and [feature branches](http://martinfowler.com/bliki/FeatureBranch.html) with issue tracking.
 
 Organizations coming to git from other version control systems frequently find it hard to develop an effective workflow.
 This article describes the GitLab flow that integrates the git workflow with an issue tracking system.
@@ -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
@@ -91,7 +91,7 @@ This workflow where commits only flow downstream ensures that everything has bee
 If you need to cherry-pick a commit with a hotfix it is common to develop it on a feature branch and merge it into master with a merge request, do not delete the feature branch.
 If master is good to go (it should be if you a practicing [continuous delivery](http://martinfowler.com/bliki/ContinuousDelivery.html)) you then merge it to the other branches.
 If this is not possible because more manual testing is required you can send merge requests from the feature branch to the downstream branches.
-An 'extreme' version of environment branches are setting up an environment for each feature branch as done by [Teatro](http://teatro.io/).
+An 'extreme' version of environment branches are setting up an environment for each feature branch as done by [Teatro](https://teatro.io/).
 
 ## Release branches with GitLab flow
 
@@ -104,7 +104,7 @@ By branching as late as possible you minimize the time you have to apply bug fix
 After a release branch is announced, only serious bug fixes are included in the release branch.
 If possible these bug fixes are first merged into master and then cherry-picked into the release branch.
 This way you can't forget to cherry-pick them into master and encounter the same bug on subsequent releases.
-This is called an 'upstream first' policy that is also practiced by [Google](http://www.chromium.org/chromium-os/chromiumos-design-docs/upstream-first) and [Red Hat](http://www.redhat.com/about/news/archive/2013/5/a-community-for-using-openstack-with-red-hat-rdo).
+This is called an 'upstream first' policy that is also practiced by [Google](https://www.chromium.org/chromium-os/chromiumos-design-docs/upstream-first) and [Red Hat](https://www.redhat.com/about/news/archive/2013/5/a-community-for-using-openstack-with-red-hat-rdo).
 Every time a bug-fix is included in a release branch the patch version is raised (to comply with [Semantic Versioning](http://semver.org/)) by setting a new tag.
 Some projects also have a stable branch that points to the same commit as the latest released branch.
 In this flow it is not common to have a production branch (or git flow master branch).
@@ -200,7 +200,7 @@ And to understand a change in context one can always look at the merge commit th
 
 After you merge multiple commits from a feature branch into the master branch this is harder to undo.
 If you would have squashed all the commits into one you could have just reverted this commit but as we indicated you should not rebase commits after they are pushed.
-Fortunately [reverting a merge made some time ago](http://git-scm.com/blog/2010/03/02/undoing-merges.html) can be done with git.
+Fortunately [reverting a merge made some time ago](https://git-scm.com/blog/2010/03/02/undoing-merges.html) can be done with git.
 This however, requires having specific merge commits for the commits your want to revert.
 If you revert a merge and you change your mind, revert the revert instead of merging again since git will not allow you to merge the code again otherwise.
 
@@ -215,7 +215,7 @@ With git you can also rebase your feature branch commits to order them after the
 This prevents creating a merge commit when merging master into your feature branch and creates a nice linear history.
 However, just like with squashing you should never rebase commits you have pushed to a remote server.
 This makes it impossible to rebase work in progress that you already shared with your team which is something we recommend.
-When using rebase to keep your feature branch updated you [need to resolve similar conflicts again and again](http://blogs.atlassian.com/2013/10/git-team-workflows-merge-or-rebase/).
+When using rebase to keep your feature branch updated you [need to resolve similar conflicts again and again](https://blogs.atlassian.com/2013/10/git-team-workflows-merge-or-rebase/).
 You can reuse recorded resolutions (rerere) sometimes, but without rebasing you only have to solve the conflicts one time and you鈥檙e set.
 There has to be a better way to avoid many merge commits.
 
@@ -307,7 +307,7 @@ When initiating a feature branch, always start with an up to date master to bran
 If you know beforehand that your work absolutely depends on another branch you can also branch from there.
 If you need to merge in another branch after starting explain the reason in the merge commit.
 If you have not pushed your commits to a shared location yet you can also rebase on master or another feature branch.
-Do not merge in upstream if your code will work and merge cleanly without doing so, Linus even says that [you should never merge in upstream at random points, only at major releases](http://lwn.net/Articles/328438/).
+Do not merge in upstream if your code will work and merge cleanly without doing so, Linus even says that [you should never merge in upstream at random points, only at major releases](https://lwn.net/Articles/328438/).
 Merging only when needed prevents creating merge commits in your feature branch that later end up littering the master history.
 
 ### References
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/workflow/importing/migrating_from_svn.md b/doc/workflow/importing/migrating_from_svn.md
index 485db4834e97c635d546cbfeb02c14e893051cfd..1938ccd0c264c42b2d42b7772f85980e1d1d262d 100644
--- a/doc/workflow/importing/migrating_from_svn.md
+++ b/doc/workflow/importing/migrating_from_svn.md
@@ -6,9 +6,9 @@ Git is a distributed version control system.
 There are some major differences between the two, for more information consult your favorite search engine.
 
 Git has tools for migrating SVN repositories to git, namely `git svn`. You can read more about this at
-[git documentation pages](http://git-scm.com/book/en/Git-and-Other-Systems-Git-and-Subversion).
+[git documentation pages](https://git-scm.com/book/en/Git-and-Other-Systems-Git-and-Subversion).
 
-Apart from the [official git documentation](http://git-scm.com/book/en/Git-and-Other-Systems-Migrating-to-Git) there is also
+Apart from the [official git documentation](https://git-scm.com/book/en/Git-and-Other-Systems-Migrating-to-Git) there is also
 user created step by step guide for migrating from SVN to GitLab.
 
 [Benjamin New](https://github.com/leftclickben) wrote [a guide that shows how to do a migration](https://gist.github.com/leftclickben/322b7a3042cbe97ed2af). Mirrors can be found [here](https://gitlab.com/snippets/2168) and [here](https://gist.github.com/maxlazio/f1b593b0d00aa966e9ca).
diff --git a/doc/workflow/merge_requests.md b/doc/workflow/merge_requests.md
index 751e19da7f19a5820bb40e8b4f2a79eb8bb7cc0c..6d57b5d98cdc656c1ab8b56080eac359ba755c00 100644
--- a/doc/workflow/merge_requests.md
+++ b/doc/workflow/merge_requests.md
@@ -38,3 +38,15 @@ To check out a particular merge request:
 ```
 $ git checkout origin/merge-requests/1
 ```
+
+## Ignore whitespace changes in Merge Request diff view
+
+![MR diff](merge_requests/merge_request_diff.png)
+
+It you add `w=1` option to URL, you can see diff without whitespace changes.
+
+![MR diff without whitespace](merge_requests/merge_request_diff_without_whitespace.png)
+
+It is also working on commits compare view.
+
+![Commit Compare](merge_requests/commit_compare.png)
diff --git a/doc/workflow/merge_requests/commit_compare.png b/doc/workflow/merge_requests/commit_compare.png
new file mode 100644
index 0000000000000000000000000000000000000000..46b3a56a59b9cdf0317d7ae8c1903a65eca2c5af
Binary files /dev/null and b/doc/workflow/merge_requests/commit_compare.png differ
diff --git a/doc/workflow/merge_requests/merge_request_diff.png b/doc/workflow/merge_requests/merge_request_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..ed08ae91bec73772c3cd51521a46edfea9aace93
Binary files /dev/null and b/doc/workflow/merge_requests/merge_request_diff.png differ
diff --git a/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png b/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png
new file mode 100644
index 0000000000000000000000000000000000000000..67d67a64d122ffa00c76a34a095f56b6f48adb01
Binary files /dev/null and b/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png differ
diff --git a/doc/workflow/milestones.md b/doc/workflow/milestones.md
new file mode 100644
index 0000000000000000000000000000000000000000..dff36899aec54234aa4fedf72d92296246160ddd
--- /dev/null
+++ b/doc/workflow/milestones.md
@@ -0,0 +1,13 @@
+# Milestones
+
+Milestones allow you to organize issues and merge requests into a cohesive group, optionally setting a due date. 
+A common use is keeping track of an upcoming software version. Milestones are created per-project.
+
+![milestone form](milestones/form.png)
+
+## Groups and milestones
+
+You can create a milestone for several projects in the same group simultaneously. 
+On the group's milestones page, you will be able to see the status of that milestone across all of the selected projects.
+
+![group milestone form](milestones/group_form.png)
diff --git a/doc/workflow/milestones/form.png b/doc/workflow/milestones/form.png
new file mode 100644
index 0000000000000000000000000000000000000000..de44c1ffc1a44c21e7d6eff77ff8752d84b6ea8f
Binary files /dev/null and b/doc/workflow/milestones/form.png differ
diff --git a/doc/workflow/milestones/group_form.png b/doc/workflow/milestones/group_form.png
new file mode 100644
index 0000000000000000000000000000000000000000..38862dcca6895c28a09c6173fc8a2d7b1552b528
Binary files /dev/null and b/doc/workflow/milestones/group_form.png differ
diff --git a/doc/workflow/releases.md b/doc/workflow/releases.md
new file mode 100644
index 0000000000000000000000000000000000000000..6176784fc57f02954250a110ca8611e981c0f2f7
--- /dev/null
+++ b/doc/workflow/releases.md
@@ -0,0 +1,20 @@
+# Releases
+
+You can turn any git tag into a release, by adding a note to it.
+Release notes behave like any other markdown form in GitLab so you can write text and drag-n-drop files to it.
+Release notes are stored in the database of GitLab. 
+
+There are several ways to add release notes: 
+
+* In the interface, when you create a new git tag with GitLab
+* In the interface, by adding a note to an existing git tag
+* with the GitLab API
+
+## New tag page with release notes text area
+
+![new_tag](releases/new_tag.png)
+
+## Tags page with button to add or edit release notes for existing git tag
+
+![tags](releases/tags.png)
+
diff --git a/doc/workflow/releases/new_tag.png b/doc/workflow/releases/new_tag.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2b64bfe17fb95f221cbd57ec3da684b5e47ee39
Binary files /dev/null and b/doc/workflow/releases/new_tag.png differ
diff --git a/doc/workflow/releases/tags.png b/doc/workflow/releases/tags.png
new file mode 100644
index 0000000000000000000000000000000000000000..aca91906c68a439d0987085db345cb82c04a51b4
Binary files /dev/null and b/doc/workflow/releases/tags.png differ
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/groups.feature b/features/groups.feature
index db37fa3b3757b3437a1cb107620a159213062137..615eff6a330607aa719bc70e6ee6d7e977572b57 100644
--- a/features/groups.feature
+++ b/features/groups.feature
@@ -153,6 +153,13 @@ Feature: Groups
     Then I should see group milestone with descriptions and expiry date
     And I should see group milestone with all issues and MRs assigned to that milestone
 
+  Scenario: Create multiple milestones with one form
+    Given I visit group "Owned" milestones page
+    And I click new milestone button
+    And I fill milestone name
+    When I press create mileston button
+    Then milestone in each project should be created
+
   # Group projects in settings
   Scenario: I should see all projects in the project list in settings
     Given Group "Owned" has archived project
@@ -169,4 +176,4 @@ Feature: Groups
     When I visit group "Owned" page
     Then I should see group "Owned"
     Then I should see project "Public-project"
-   
+
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 6b0484b6a38ebcbb95315d5ba093489a6b05f7e2..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
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/groups.rb b/features/steps/groups.rb
index 69ddfa42c062df0d9f9b3d49b0c6bdd889d6ec46..a8fba2406ae0d0cd8ce54fcc2304d836498eaf4f 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
@@ -255,6 +255,28 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
     expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived')
   end
 
+  step 'I fill milestone name' do
+    fill_in 'milestone_title', with: 'v2.9.0'
+  end
+
+  step 'I click new milestone button' do
+    click_link "New Milestone"
+  end
+
+  step 'I press create mileston button' do
+    click_button "Create Milestone"
+  end
+
+  step 'milestone in each project should be created' do
+    group = Group.find_by(name: 'Owned')
+    expect(page).to have_content "Milestone v2.9.0"
+    expect(group.projects).to be_present
+
+    group.projects.each do |project|
+      expect(page).to have_content project.name
+    end
+  end
+
   protected
 
   def assigned_to_me(key)
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/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 1b27500497aed85d395f09052d162c7c15390965..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))
@@ -335,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/paths.rb b/features/steps/shared/paths.rb
index eb978620da62f20d7f046f17945c8ea51fdaa467..c74a5fd3bc7e7cfa7d044ccb3182622847b0baa6 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -31,6 +31,10 @@ module SharedPaths
     visit merge_requests_group_path(Group.find_by(name: "Owned"))
   end
 
+  step 'I visit group "Owned" milestones page' do
+    visit group_milestones_path(Group.find_by(name: "Owned"))
+  end
+
   step 'I visit group "Owned" members page' do
     visit group_group_members_path(Group.find_by(name: "Owned"))
   end
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..fe1bf8a48160c1fd4fe244925d227e8ae9db9597 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
@@ -52,5 +52,6 @@ module API
     mount Labels
     mount Settings
     mount Keys
+    mount Tags
   end
 end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 883a5e14b17cea75fc45ed50d82b9fadf7533c19..d6aec03d7f557fab6d3539388ca9db17b61529c6 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -62,7 +62,7 @@ module API
       expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
       expose :name, :name_with_namespace
       expose :path, :path_with_namespace
-      expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at
+      expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :created_at, :last_activity_at
       expose :creator_id
       expose :namespace
       expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? }
@@ -95,25 +95,6 @@ module API
       end
     end
 
-    class RepoTag < Grape::Entity
-      expose :name
-      expose :message do |repo_obj, _options|
-        if repo_obj.respond_to?(:message)
-          repo_obj.message
-        else
-          nil
-        end
-      end
-
-      expose :commit do |repo_obj, options|
-        if repo_obj.respond_to?(:commit)
-          repo_obj.commit
-        elsif options[:project]
-          options[:project].repository.commit(repo_obj.target)
-        end
-      end
-    end
-
     class RepoObject < Grape::Entity
       expose :name
 
@@ -231,7 +212,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
 
@@ -341,5 +322,34 @@ module API
       expose :user_oauth_applications
       expose :after_sign_out_path
     end
+
+    class Release < Grape::Entity
+      expose :tag, :description
+    end
+
+    class RepoTag < Grape::Entity
+      expose :name
+      expose :message do |repo_obj, _options|
+        if repo_obj.respond_to?(:message)
+          repo_obj.message
+        else
+          nil
+        end
+      end
+
+      expose :commit do |repo_obj, options|
+        if repo_obj.respond_to?(:commit)
+          repo_obj.commit
+        elsif options[:project]
+          options[:project].repository.commit(repo_obj.target)
+        end
+      end
+
+      expose :release, using: Entities::Release do |repo_obj, options|
+        if options[:project]
+          options[:project].releases.find_by(tag: repo_obj.name)
+        end
+      end
+    end
   end
 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..92540ccf2b106bb09cbaba7a85972e21dabfa20e 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"
@@ -133,6 +133,12 @@ module API
       authorize! :admin_project, user_project
     end
 
+    def require_gitlab_workhorse!
+      unless env['HTTP_GITLAB_WORKHORSE'].present?
+        forbidden!('Request should be executed via GitLab Workhorse')
+      end
+    end
+
     def can?(object, action, subject)
       abilities.allowed?(object, action, subject)
     end
@@ -234,6 +240,10 @@ module API
       render_api_error!(message || '409 Conflict', 409)
     end
 
+    def file_to_large!
+      render_api_error!('413 Request Entity Too Large', 413)
+    end
+
     def render_validation_error!(model)
       if model.errors.any?
         render_api_error!(model.errors.messages || '400 Bad Request', 400)
@@ -282,6 +292,44 @@ module API
       end
     end
 
+    # file helpers
+
+    def uploaded_file!(field, uploads_path)
+      if params[field]
+        bad_request!("#{field} is not a file") unless params[field].respond_to?(:filename)
+        return params[field]
+      end
+
+      # sanitize file paths
+      # this requires all paths to exist
+      required_attributes! %W(#{field}.path)
+      uploads_path = File.realpath(uploads_path)
+      file_path = File.realpath(params["#{field}.path"])
+      bad_request!('Bad file path') unless file_path.start_with?(uploads_path)
+
+      UploadedFile.new(
+        file_path,
+        params["#{field}.name"],
+        params["#{field}.type"] || 'application/octet-stream',
+      )
+    end
+
+    def present_file!(path, filename, content_type = 'application/octet-stream')
+      filename ||= File.basename(path)
+      header['Content-Disposition'] = "attachment; filename=#{filename}"
+      header['Content-Transfer-Encoding'] = 'binary'
+      content_type content_type
+
+      # Support download acceleration
+      case headers['X-Sendfile-Type']
+      when 'X-Sendfile'
+        header['X-Sendfile'] = path
+        body
+      else
+        file FileStreamer.new(path)
+      end
+    end
+
     private
 
     def add_pagination_headers(paginated, per_page)
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index c2fb36b41433eeffe4d3fa568f9777d7e137bcaf..2b4ada6e2ebbf2e3861128d0788b0adf78284308 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -75,6 +75,7 @@ module API
       #   description (optional) - short project description
       #   issues_enabled (optional)
       #   merge_requests_enabled (optional)
+      #   builds_enabled (optional)
       #   wiki_enabled (optional)
       #   snippets_enabled (optional)
       #   namespace_id (optional) - defaults to user namespace
@@ -90,6 +91,7 @@ module API
                                      :description,
                                      :issues_enabled,
                                      :merge_requests_enabled,
+                                     :builds_enabled,
                                      :wiki_enabled,
                                      :snippets_enabled,
                                      :namespace_id,
@@ -117,6 +119,7 @@ module API
       #   default_branch (optional) - 'master' by default
       #   issues_enabled (optional)
       #   merge_requests_enabled (optional)
+      #   builds_enabled (optional)
       #   wiki_enabled (optional)
       #   snippets_enabled (optional)
       #   public (optional) - if true same as setting visibility_level = 20
@@ -132,6 +135,7 @@ module API
                                      :default_branch,
                                      :issues_enabled,
                                      :merge_requests_enabled,
+                                     :builds_enabled,
                                      :wiki_enabled,
                                      :snippets_enabled,
                                      :public,
@@ -172,6 +176,7 @@ module API
       #   description (optional) - short project description
       #   issues_enabled (optional)
       #   merge_requests_enabled (optional)
+      #   builds_enabled (optional)
       #   wiki_enabled (optional)
       #   snippets_enabled (optional)
       #   public (optional) - if true same as setting visibility_level = 20
@@ -185,6 +190,7 @@ module API
                                      :default_branch,
                                      :issues_enabled,
                                      :merge_requests_enabled,
+                                     :builds_enabled,
                                      :wiki_enabled,
                                      :snippets_enabled,
                                      :public,
@@ -246,8 +252,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 20d568cf4626ed9b7b292a11c066c30b0daccc6e..d7c48639eba42d1b195c2b2121ef401d3ca040ce 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -16,41 +16,6 @@ module API
         end
       end
 
-      # Get a project repository tags
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   GET /projects/:id/repository/tags
-      get ":id/repository/tags" do
-        present user_project.repo.tags.sort_by(&:name).reverse,
-                with: Entities::RepoTag, project: user_project
-      end
-
-      # Create tag
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      #   tag_name (required) - The name of the tag
-      #   ref (required) - Create tag from commit sha or branch
-      #   message (optional) - Specifying a message creates an annotated tag.
-      # Example Request:
-      #   POST /projects/:id/repository/tags
-      post ':id/repository/tags' do
-        authorize_push_project
-        message = params[:message] || nil
-        result = CreateTagService.new(user_project, current_user).
-          execute(params[:tag_name], params[:ref], message)
-
-        if result[:status] == :success
-          present result[:tag],
-                  with: Entities::RepoTag,
-                  project: user_project
-        else
-          render_api_error!(result[:message], 400)
-        end
-      end
-
       # Get a project repository tree
       #
       # Parameters:
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
new file mode 100644
index 0000000000000000000000000000000000000000..673342dd44714b7d6898b74dd983d55ed309c8d1
--- /dev/null
+++ b/lib/api/tags.rb
@@ -0,0 +1,61 @@
+module API
+  # Git Tags API
+  class Tags < Grape::API
+    before { authenticate! }
+    before { authorize! :download_code, user_project }
+
+    resource :projects do
+      # Get a project repository tags
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      # Example Request:
+      #   GET /projects/:id/repository/tags
+      get ":id/repository/tags" do
+        present user_project.repo.tags.sort_by(&:name).reverse,
+                with: Entities::RepoTag, project: user_project
+      end
+
+      # Create tag
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   tag_name (required) - The name of the tag
+      #   ref (required) - Create tag from commit sha or branch
+      #   message (optional) - Specifying a message creates an annotated tag.
+      # Example Request:
+      #   POST /projects/:id/repository/tags
+      post ':id/repository/tags' do
+        authorize_push_project
+        message = params[:message] || nil
+        result = CreateTagService.new(user_project, current_user).
+          execute(params[:tag_name], params[:ref], message, params[:release_description])
+
+        if result[:status] == :success
+          present result[:tag],
+                  with: Entities::RepoTag,
+                  project: user_project
+        else
+          render_api_error!(result[:message], 400)
+        end
+      end
+
+      # Add release notes to tag
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   tag (required) - The name of the tag
+      #   description (required) - Release notes with markdown support
+      # Example Request:
+      #   PUT /projects/:id/repository/tags
+      put ':id/repository/:tag/release', requirements: { tag: /.*/ } do
+        authorize_push_project
+        required_attributes! [:description]
+        release = user_project.releases.find_or_initialize_by(tag: params[:tag])
+        release.update_attributes(description: params[:description])
+
+        present release, with: Entities::Release
+      end
+    end
+  end
+end
diff --git a/lib/backup/artifacts.rb b/lib/backup/artifacts.rb
new file mode 100644
index 0000000000000000000000000000000000000000..51fa3867e67909911f8671abfae5a79081e59c3b
--- /dev/null
+++ b/lib/backup/artifacts.rb
@@ -0,0 +1,13 @@
+require 'backup/files'
+
+module Backup
+  class Artifacts < Files
+    def initialize
+      super('artifacts', ArtifactUploader.artifacts_path)
+    end
+
+    def create_files_dir
+      Dir.mkdir(app_files_dir, 0700)
+    end
+  end
+end
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..9e15d5411a1742760434937936ddd2fa1621c99b 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", "artifacts.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..07e68216d7f1d08876f09545cd426bac8f787323 100644
--- a/lib/ci/api/api.rb
+++ b/lib/ci/api/api.rb
@@ -26,7 +26,8 @@ module Ci
       format :json
 
       helpers Helpers
-      helpers ::API::APIHelpers
+      helpers ::API::Helpers
+      helpers Gitlab::CurrentSettings
 
       mount Builds
       mount Commits
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index 83ca1e6481c8761722a76355471993835a9f8e16..0a58667280745de66b4fc6f89488d55fa796516d 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -47,6 +47,106 @@ module Ci
             build.drop
           end
         end
+
+        # Authorize artifacts uploading for build - Runners only
+        #
+        # Parameters:
+        #   id (required) - The ID of a build
+        #   token (required) - The build authorization token
+        #   filesize (optional) - the size of uploaded file
+        # Example Request:
+        #   POST /builds/:id/artifacts/authorize
+        post ":id/artifacts/authorize" do
+          require_gitlab_workhorse!
+          build = Ci::Build.find_by_id(params[:id])
+          not_found! unless build
+          authenticate_build_token!(build)
+          forbidden!('build is not running') unless build.running?
+
+          if params[:filesize]
+            file_size = params[:filesize].to_i
+            file_to_large! unless file_size < max_artifacts_size
+          end
+
+          status 200
+          { TempPath: ArtifactUploader.artifacts_upload_path }
+        end
+
+        # Upload artifacts to build - Runners only
+        #
+        # Parameters:
+        #   id (required) - The ID of a build
+        #   token (required) - The build authorization token
+        #   file (required) - The uploaded file
+        # Parameters (accelerated by GitLab Workhorse):
+        #   file.path - path to locally stored body (generated by Workhorse)
+        #   file.name - real filename as send in Content-Disposition
+        #   file.type - real content type as send in Content-Type
+        # Headers:
+        #   BUILD-TOKEN (required) - The build authorization token, the same as token
+        # Body:
+        #   The file content
+        #
+        # Example Request:
+        #   POST /builds/:id/artifacts
+        post ":id/artifacts" do
+          require_gitlab_workhorse!
+          build = Ci::Build.find_by_id(params[:id])
+          not_found! unless build
+          authenticate_build_token!(build)
+          forbidden!('build is not running') unless build.running?
+
+          file = uploaded_file!(:file, ArtifactUploader.artifacts_upload_path)
+          file_to_large! unless file.size < max_artifacts_size
+
+          if build.update_attributes(artifacts_file: file)
+            present build, with: Entities::Build
+          else
+            render_validation_error!(build)
+          end
+        end
+
+        # Download the artifacts file from build - Runners only
+        #
+        # Parameters:
+        #   id (required) - The ID of a build
+        #   token (required) - The build authorization token
+        # Headers:
+        #   BUILD-TOKEN (required) - The build authorization token, the same as token
+        # Example Request:
+        #   GET /builds/:id/artifacts
+        get ":id/artifacts" do
+          build = Ci::Build.find_by_id(params[:id])
+          not_found! unless build
+          authenticate_build_token!(build)
+          artifacts_file = build.artifacts_file
+
+          unless artifacts_file.file_storage?
+            return redirect_to build.artifacts_file.url
+          end
+
+          unless artifacts_file.exists?
+            not_found!
+          end
+
+          present_file!(artifacts_file.path, artifacts_file.filename)
+        end
+
+        # Remove the artifacts file from build
+        #
+        # Parameters:
+        #   id (required) - The ID of a build
+        #   token (required) - The build authorization token
+        # Headers:
+        #   BUILD-TOKEN (required) - The build authorization token, the same as token
+        # Example Request:
+        #   DELETE /builds/:id/artifacts
+        delete ":id/artifacts" do
+          build = Ci::Build.find_by_id(params[:id])
+          not_found! unless build
+          authenticate_build_token!(build)
+          build.remove_artifacts_file!
+        end
       end
     end
   end
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index b80c0b8b2735427e5703f2175cb939c03e4b1a26..750f421872deea8e4c07788523d541e118e17095 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -11,10 +11,16 @@ module Ci
         expose :builds
       end
 
+      class ArtifactFile < Grape::Entity
+        expose :filename, :size
+      end
+
       class Build < Grape::Entity
         expose :id, :commands, :ref, :sha, :status, :project_id, :repo_url,
           :before_sha, :allow_git_fetch, :project_name
 
+        expose :name, :token, :stage
+
         expose :options do |model|
           model.options
         end
@@ -24,6 +30,7 @@ module Ci
         end
 
         expose :variables
+        expose :artifacts_file, using: ArtifactFile
       end
 
       class Runner < Grape::Entity
diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb
index e602cda81d610402356e8e0654752c8ddecb1fdf..02502333756350c33372e1eddca17c6679a3e011 100644
--- a/lib/ci/api/helpers.rb
+++ b/lib/ci/api/helpers.rb
@@ -1,6 +1,8 @@
 module Ci
   module API
     module Helpers
+      BUILD_TOKEN_HEADER = "HTTP_BUILD_TOKEN"
+      BUILD_TOKEN_PARAM = :token
       UPDATE_RUNNER_EVERY = 60
 
       def authenticate_runners!
@@ -15,8 +17,15 @@ module Ci
         forbidden! unless project.valid_token?(params[:project_token])
       end
 
+      def authenticate_build_token!(build)
+        token = (params[BUILD_TOKEN_PARAM] || env[BUILD_TOKEN_HEADER]).to_s
+        forbidden! unless token && build.valid_token?(token)
+      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
@@ -30,6 +39,10 @@ module Ci
         info = attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"])
         current_runner.update(info)
       end
+
+      def max_artifacts_size
+        current_application_settings.max_artifacts_size.megabytes.to_i
+      end
     end
   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..3beafcad117f19cbaafa13f432c632d1b65ed894 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -4,13 +4,14 @@ 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_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables, :cache]
+    ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts, :cache]
 
-    attr_reader :before_script, :image, :services, :variables
+    attr_reader :before_script, :image, :services, :variables, :path, :cache
 
-    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"
@@ -45,6 +46,7 @@ module Ci
       @services = @config[:services]
       @stages = @config[:stages] || @config[:types]
       @variables = @config[:variables] || {}
+      @cache = @config[:cache]
       @config.except!(*ALLOWED_YAML_KEYS)
 
       # anything that doesn't have script is considered as unknown
@@ -63,26 +65,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,21 +75,16 @@ 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
+          services: job[:services] || @services,
+          artifacts: job[:artifacts],
+          cache: job[:cache] || @cache,
         }.compact
       }
     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")
@@ -137,63 +114,140 @@ module Ci
         raise ValidationError, "variables should be a map of key-valued strings"
       end
 
+      if @cache
+        if @cache[:untracked] && !validate_boolean(@cache[:untracked])
+          raise ValidationError, "cache:untracked parameter should be an boolean"
+        end
+
+        if @cache[:paths] && !validate_array_of_strings(@cache[:paths])
+          raise ValidationError, "cache:paths parameter should be an array of strings"
+        end
+      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"
+      if job[:cache]
+        if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked])
+          raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean"
+        end
+
+        if job[:cache][:paths] && !validate_array_of_strings(job[:cache][:paths])
+          raise ValidationError, "#{name} job: cache:paths parameter should be an array of strings"
+        end
+      end
+
+      if job[:artifacts]
+        if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked])
+          raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean"
+        end
+
+        if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths])
+          raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings"
+        end
+      end
+
+      if job[:allow_failure] && !validate_boolean(job[:allow_failure])
+        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 validate_boolean(value)
+      value.in?([true, false])
+    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/file_streamer.rb b/lib/file_streamer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4e3c6d3c773ac1238184e137316ed7bf79a218dd
--- /dev/null
+++ b/lib/file_streamer.rb
@@ -0,0 +1,16 @@
+class FileStreamer #:nodoc:
+  attr_reader :to_path
+
+  def initialize(path)
+    @to_path = path
+  end
+
+  # Stream the file's contents if Rack::Sendfile isn't present.
+  def each
+    File.open(to_path, 'rb') do |file|
+      while chunk = file.read(16384)
+        yield chunk
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 6830a916bcb619c8ce9b3f691ab8322a7e8910d4..0d156047ff09f7318542db1aaf44525172152cef 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -33,8 +33,11 @@ module Grack
 
       auth!
 
+      lfs_response = Gitlab::Lfs::Router.new(project, @user, @request).try_call
+      return lfs_response unless lfs_response.nil?
+
       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
@@ -72,7 +75,7 @@ module Grack
       matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login)
 
       if project && matched_login.present? && git_cmd == 'git-upload-pack'
-        underscored_service = matched_login['s'].underscore 
+        underscored_service = matched_login['s'].underscore
 
         if Service.available_services_names.include?(underscored_service)
           service_method = "#{underscored_service}_service"
@@ -193,12 +196,19 @@ module Grack
     end
 
     def render_grack_auth_ok
+      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' => project.repository.path_to_repo,
+          'RepoPath' => repo_path,
         })]
       ]
     end
diff --git a/lib/gitlab/compare_result.rb b/lib/gitlab/compare_result.rb
index d72391dade5f025022bcc4974ca0e1ec8db2751d..0d696a1ee282a298a89aac585b0813e27edca777 100644
--- a/lib/gitlab/compare_result.rb
+++ b/lib/gitlab/compare_result.rb
@@ -2,8 +2,8 @@ module Gitlab
   class CompareResult
     attr_reader :commits, :diffs
 
-    def initialize(compare)
-      @commits, @diffs = compare.commits, compare.diffs
+    def initialize(compare, diff_options = {})
+      @commits, @diffs = compare.commits, compare.diffs(nil, diff_options)
     end
   end
 end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 0ea1b6a2f6f4f86923dcd324077ce624e6886dae..2d3e32d95396111ba51b887bf49c504090f5d056 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -23,7 +23,9 @@ 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'],
+        max_artifacts_size: Ci::Settings.gitlab_ci['max_artifacts_size'],
       )
     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_access.rb b/lib/gitlab/git_access.rb
index c90184d31cf1c3899ff65df28da10ff388b480bc..3ed1eec517c0143b204ae59c793e268ea483a1c0 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -13,7 +13,7 @@ module Gitlab
     def user
       return @user if defined?(@user)
 
-      @user = 
+      @user =
         case actor
         when User
           actor
@@ -125,7 +125,7 @@ module Gitlab
     def change_access_check(change)
       oldrev, newrev, ref = change.split(' ')
 
-      action = 
+      action =
         if project.protected_branch?(branch_name(ref))
           protected_branch_action(oldrev, newrev, branch_name(ref))
         elsif protected_tag?(tag_name(ref))
@@ -148,7 +148,7 @@ module Gitlab
             build_status_object(false, "You are not allowed to change existing tags on this project.")
           else # :push_code
             build_status_object(false, "You are not allowed to push code to this project.")
-          end 
+          end
         return status
       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/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb
index 03c410726a577fa98f4d95fd624642bf033b7c8e..87fee28dc010adfcae54c903c427a67d8634b54a 100644
--- a/lib/gitlab/google_code_import/importer.rb
+++ b/lib/gitlab/google_code_import/importer.rb
@@ -30,7 +30,7 @@ module Gitlab
 
       def user_map
         @user_map ||= begin
-          user_map = Hash.new do |hash, user| 
+          user_map = Hash.new do |hash, user|
             # Replace ... by \.\.\., so `johnsm...@gmail.com` isn't autolinked.
             Client.mask_email(user).sub("...", "\\.\\.\\.")
           end
@@ -76,18 +76,7 @@ module Gitlab
           attachments = format_attachments(raw_issue["id"], 0, issue_comment["attachments"])
 
           body = format_issue_body(author, date, content, attachments)
-
-          labels = []
-          raw_issue["labels"].each do |label|
-            name = nice_label_name(label)
-            labels << name
-
-            unless @known_labels.include?(name)
-              create_label(name)
-              @known_labels << name
-            end
-          end
-          labels << nice_status_name(raw_issue["status"])
+          labels = import_issue_labels(raw_issue)
 
           assignee_id = nil
           if raw_issue.has_key?("owner")
@@ -110,6 +99,7 @@ module Gitlab
             assignee_id:  assignee_id,
             state:        raw_issue["state"] == "closed" ? "closed" : "opened"
           )
+
           issue.add_labels_by_names(labels)
 
           if issue.iid != raw_issue["id"]
@@ -120,6 +110,23 @@ module Gitlab
         end
       end
 
+      def import_issue_labels(raw_issue)
+        labels = []
+
+        raw_issue["labels"].each do |label|
+          name = nice_label_name(label)
+          labels << name
+
+          unless @known_labels.include?(name)
+            create_label(name)
+            @known_labels << name
+          end
+        end
+
+        labels << nice_status_name(raw_issue["status"])
+        labels
+      end
+
       def import_issue_comments(issue, comments)
         Note.transaction do
           while raw_comment = comments.shift
@@ -172,7 +179,7 @@ module Gitlab
           "#5cb85c"
         when "Status: Started"
           "#8e44ad"
-        
+
         when "Priority: Critical"
           "#ffcfcf"
         when "Priority: High"
@@ -181,7 +188,7 @@ module Gitlab
           "#fff5cc"
         when "Priority: Low"
           "#cfe9ff"
-        
+
         when "Type: Defect"
           "#d9534f"
         when "Type: Enhancement"
@@ -249,8 +256,8 @@ module Gitlab
         end
 
         if raw_updates.has_key?("cc")
-          cc = raw_updates["cc"].map do |l| 
-            deleted = l.start_with?("-") 
+          cc = raw_updates["cc"].map do |l|
+            deleted = l.start_with?("-")
             l = l[1..-1] if deleted
             l = user_map[l]
             l = "~~#{l}~~" if deleted
@@ -261,8 +268,8 @@ module Gitlab
         end
 
         if raw_updates.has_key?("labels")
-          labels = raw_updates["labels"].map do |l| 
-            deleted = l.start_with?("-") 
+          labels = raw_updates["labels"].map do |l|
+            deleted = l.start_with?("-")
             l = l[1..-1] if deleted
             l = nice_label_name(l)
             l = "~~#{l}~~" if deleted
@@ -278,45 +285,39 @@ module Gitlab
 
         if raw_updates.has_key?("blockedOn")
           blocked_ons = raw_updates["blockedOn"].map do |raw_blocked_on|
-            name, id = raw_blocked_on.split(":", 2)
-
-            deleted = name.start_with?("-") 
-            name = name[1..-1] if deleted
-
-            text =
-              if name == project.import_source
-                "##{id}"
-              else
-                "#{project.namespace.path}/#{name}##{id}"
-              end
-            text = "~~#{text}~~" if deleted
-            text
+            format_blocking_updates(raw_blocked_on)
           end
+
           updates << "*Blocked on: #{blocked_ons.join(", ")}*"
         end
 
         if raw_updates.has_key?("blocking")
           blockings = raw_updates["blocking"].map do |raw_blocked_on|
-            name, id = raw_blocked_on.split(":", 2)
-            
-            deleted = name.start_with?("-") 
-            name = name[1..-1] if deleted
-
-            text =
-              if name == project.import_source
-                "##{id}"
-              else
-                "#{project.namespace.path}/#{name}##{id}"
-              end
-            text = "~~#{text}~~" if deleted
-            text
+            format_blocking_updates(raw_blocked_on)
           end
+
           updates << "*Blocking: #{blockings.join(", ")}*"
         end
 
         updates
       end
 
+      def format_blocking_updates(raw_blocked_on)
+        name, id = raw_blocked_on.split(":", 2)
+
+        deleted = name.start_with?("-")
+        name = name[1..-1] if deleted
+
+        text =
+          if name == project.import_source
+            "##{id}"
+          else
+            "#{project.namespace.path}/#{name}##{id}"
+          end
+        text = "~~#{text}~~" if deleted
+        text
+      end
+
       def format_attachments(issue_id, comment_id, raw_attachments)
         return [] unless raw_attachments
 
@@ -325,7 +326,7 @@ module Gitlab
 
           filename = attachment["fileName"]
           link = "https://storage.googleapis.com/google-code-attachments/#{@repo.name}/issue-#{issue_id}/comment-#{comment_id}/#{filename}"
-          
+
           text = "[#{filename}](#{link})"
           text = "!#{text}" if filename =~ /\.(png|jpg|jpeg|gif|bmp|tiff)\z/i
           text
diff --git a/lib/gitlab/inline_diff.rb b/lib/gitlab/inline_diff.rb
index 99e7b529ba9534c6612baea3f110db08520322b2..44507bde25deaef0e8f16adab6ca7af2670c99d7 100644
--- a/lib/gitlab/inline_diff.rb
+++ b/lib/gitlab/inline_diff.rb
@@ -11,48 +11,71 @@ module Gitlab
         indexes.each do |index|
           first_line = diff_arr[index+1]
           second_line = diff_arr[index+2]
-          max_length = [first_line.size, second_line.size].max
 
           # Skip inline diff if empty line was replaced with content
           next if first_line == "-\n"
 
-          first_the_same_symbols = 0
-          (0..max_length + 1).each do |i|
-            first_the_same_symbols = i - 1
-            if first_line[i] != second_line[i] && i > 0
-              break
-            end
-          end
+          first_token = find_first_token(first_line, second_line)
+          apply_first_token(diff_arr, index, first_token)
+
+          last_token = find_last_token(first_line, second_line, first_token)
+          apply_last_token(diff_arr, index, last_token)
+        end
+
+        diff_arr
+      end
+
+      def apply_first_token(diff_arr, index, first_token)
+        start = first_token + START
+
+        if first_token.empty?
+          # In case if we remove string of spaces in commit
+          diff_arr[index+1].sub!("-", "-" => "-#{START}")
+          diff_arr[index+2].sub!("+", "+" => "+#{START}")
+        else
+          diff_arr[index+1].sub!(first_token, first_token => start)
+          diff_arr[index+2].sub!(first_token, first_token => start)
+        end
+      end
 
-          first_token = first_line[0..first_the_same_symbols][1..-1]
-          start = first_token + START
+      def apply_last_token(diff_arr, index, last_token)
+        # This is tricky: escape backslashes so that `sub` doesn't interpret them
+        # as backreferences. Regexp.escape does NOT do the right thing.
+        replace_token = FINISH + last_token.gsub(/\\/, '\&\&')
+        diff_arr[index+1].sub!(/#{Regexp.escape(last_token)}$/, replace_token)
+        diff_arr[index+2].sub!(/#{Regexp.escape(last_token)}$/, replace_token)
+      end
+
+      def find_first_token(first_line, second_line)
+        max_length = [first_line.size, second_line.size].max
+        first_the_same_symbols = 0
+
+        (0..max_length + 1).each do |i|
+          first_the_same_symbols = i - 1
 
-          if first_token.empty?
-            # In case if we remove string of spaces in commit
-            diff_arr[index+1].sub!("-", "-" => "-#{START}")
-            diff_arr[index+2].sub!("+", "+" => "+#{START}")
-          else
-            diff_arr[index+1].sub!(first_token, first_token => start)
-            diff_arr[index+2].sub!(first_token, first_token => start)
+          if first_line[i] != second_line[i] && i > 0
+            break
           end
+        end
+
+        first_line[0..first_the_same_symbols][1..-1]
+      end
+
+      def find_last_token(first_line, second_line, first_token)
+        max_length = [first_line.size, second_line.size].max
+        last_the_same_symbols = 0
+
+        (1..max_length + 1).each do |i|
+          last_the_same_symbols = -i
+          shortest_line = second_line.size > first_line.size ? first_line : second_line
 
-          last_the_same_symbols = 0
-          (1..max_length + 1).each do |i|
-            last_the_same_symbols = -i
-            shortest_line = second_line.size > first_line.size ? first_line : second_line
-            if ( first_line[-i] != second_line[-i] ) || "#{first_token}#{START}".size == shortest_line[1..-i].size
-              break
-            end
+          if (first_line[-i] != second_line[-i]) || "#{first_token}#{START}".size == shortest_line[1..-i].size
+            break
           end
-          last_the_same_symbols += 1
-          last_token = first_line[last_the_same_symbols..-1]
-          # This is tricky: escape backslashes so that `sub` doesn't interpret them
-          # as backreferences. Regexp.escape does NOT do the right thing.
-          replace_token = FINISH + last_token.gsub(/\\/, '\&\&')
-          diff_arr[index+1].sub!(/#{Regexp.escape(last_token)}$/, replace_token)
-          diff_arr[index+2].sub!(/#{Regexp.escape(last_token)}$/, replace_token)
         end
-        diff_arr
+
+        last_the_same_symbols += 1
+        first_line[last_the_same_symbols..-1]
       end
 
       def _indexes_of_changed_lines(diff_arr)
diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4202c786466849b66bad3ca05ea57df866eeca74
--- /dev/null
+++ b/lib/gitlab/lfs/response.rb
@@ -0,0 +1,308 @@
+module Gitlab
+  module Lfs
+    class Response
+
+      def initialize(project, user, request)
+        @origin_project = project
+        @project = storage_project(project)
+        @user = user
+        @env = request.env
+        @request = request
+      end
+
+      # Return a response for a download request
+      # Can be a response to:
+      # Request from a user to get the file
+      # Request from gitlab-workhorse which file to serve to the user
+      def render_download_hypermedia_response(oid)
+        render_response_to_download do
+          if check_download_accept_header?
+            render_lfs_download_hypermedia(oid)
+          else
+            render_not_found
+          end
+        end
+      end
+
+      def render_download_object_response(oid)
+        render_response_to_download do
+          if check_download_sendfile_header? && check_download_accept_header?
+            render_lfs_sendfile(oid)
+          else
+            render_not_found
+          end
+        end
+      end
+
+      def render_lfs_api_auth
+        render_response_to_push do
+          request_body = JSON.parse(@request.body.read)
+          return render_not_found if request_body.empty? || request_body['objects'].empty?
+
+          response = build_response(request_body['objects'])
+          [
+            200,
+            {
+              "Content-Type" => "application/json; charset=utf-8",
+              "Cache-Control" => "private",
+            },
+            [JSON.dump(response)]
+          ]
+        end
+      end
+
+      def render_storage_upload_authorize_response(oid, size)
+        render_response_to_push do
+          [
+            200,
+            { "Content-Type" => "application/json; charset=utf-8" },
+            [JSON.dump({
+              'StoreLFSPath' => "#{Gitlab.config.lfs.storage_path}/tmp/upload",
+              'LfsOid' => oid,
+              'LfsSize' => size
+            })]
+          ]
+        end
+      end
+
+      def render_storage_upload_store_response(oid, size, tmp_file_name)
+        render_response_to_push do
+          render_lfs_upload_ok(oid, size, tmp_file_name)
+        end
+      end
+
+      private
+
+      def render_not_enabled
+        [
+          501,
+          {
+            "Content-Type" => "application/vnd.git-lfs+json",
+          },
+          [JSON.dump({
+            'message' => 'Git LFS is not enabled on this GitLab server, contact your admin.',
+            'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
+          })]
+        ]
+      end
+
+      def render_unauthorized
+        [
+          401,
+          {
+            'Content-Type' => 'text/plain'
+          },
+          ['Unauthorized']
+        ]
+      end
+
+      def render_not_found
+        [
+          404,
+          {
+            "Content-Type" => "application/vnd.git-lfs+json"
+          },
+          [JSON.dump({
+            'message' => 'Not found.',
+            'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
+          })]
+        ]
+      end
+
+      def render_forbidden
+        [
+          403,
+          {
+            "Content-Type" => "application/vnd.git-lfs+json"
+          },
+          [JSON.dump({
+            'message' => 'Access forbidden. Check your access level.',
+            'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
+          })]
+        ]
+      end
+
+      def render_lfs_sendfile(oid)
+        return render_not_found unless oid.present?
+
+        lfs_object = object_for_download(oid)
+
+        if lfs_object && lfs_object.file.exists?
+          [
+            200,
+            {
+              # GitLab-workhorse will forward Content-Type header
+              "Content-Type" => "application/octet-stream",
+              "X-Sendfile" => lfs_object.file.path
+            },
+            []
+          ]
+        else
+          render_not_found
+        end
+      end
+
+      def render_lfs_download_hypermedia(oid)
+        return render_not_found unless oid.present?
+
+        lfs_object = object_for_download(oid)
+        if lfs_object
+          [
+            200,
+            { "Content-Type" => "application/vnd.git-lfs+json" },
+            [JSON.dump(download_hypermedia(oid))]
+          ]
+        else
+          render_not_found
+        end
+      end
+
+      def render_lfs_upload_ok(oid, size, tmp_file)
+        if store_file(oid, size, tmp_file)
+          [
+            200,
+            {
+              'Content-Type' => 'text/plain',
+              'Content-Length' => 0
+            },
+            []
+          ]
+        else
+          [
+            422,
+            { 'Content-Type' => 'text/plain' },
+            ["Unprocessable entity"]
+          ]
+        end
+      end
+
+      def render_response_to_download
+        return render_not_enabled unless Gitlab.config.lfs.enabled
+
+        unless @project.public?
+          return render_unauthorized unless @user
+          return render_forbidden unless user_can_fetch?
+        end
+
+        yield
+      end
+
+      def render_response_to_push
+        return render_not_enabled unless Gitlab.config.lfs.enabled
+        return render_unauthorized unless @user
+        return render_forbidden unless user_can_push?
+
+        yield
+      end
+
+      def check_download_sendfile_header?
+        @env['HTTP_X_SENDFILE_TYPE'].to_s == "X-Sendfile"
+      end
+
+      def check_download_accept_header?
+        @env['HTTP_ACCEPT'].to_s == "application/vnd.git-lfs+json; charset=utf-8"
+      end
+
+      def user_can_fetch?
+        # Check user access against the project they used to initiate the pull
+        @user.can?(:download_code, @origin_project)
+      end
+
+      def user_can_push?
+        # Check user access against the project they used to initiate the push
+        @user.can?(:push_code, @origin_project)
+      end
+
+      def storage_project(project)
+        if project.forked?
+          project.forked_from_project
+        else
+          project
+        end
+      end
+
+      def store_file(oid, size, tmp_file)
+        tmp_file_path = File.join("#{Gitlab.config.lfs.storage_path}/tmp/upload", tmp_file)
+
+        object = LfsObject.find_or_create_by(oid: oid, size: size)
+        if object.file.exists?
+          success = true
+        else
+          success = move_tmp_file_to_storage(object, tmp_file_path)
+        end
+
+        if success
+          success = link_to_project(object)
+        end
+
+        success
+      ensure
+        # Ensure that the tmp file is removed
+        FileUtils.rm_f(tmp_file_path)
+      end
+
+      def object_for_download(oid)
+        @project.lfs_objects.find_by(oid: oid)
+      end
+
+      def move_tmp_file_to_storage(object, path)
+        File.open(path) do |f|
+          object.file = f
+        end
+
+        object.file.store!
+        object.save
+      end
+
+      def link_to_project(object)
+        if object && !object.projects.exists?(@project)
+          object.projects << @project
+          object.save
+        end
+      end
+
+      def select_existing_objects(objects)
+        objects_oids = objects.map { |o| o['oid'] }
+        @project.lfs_objects.where(oid: objects_oids).pluck(:oid).to_set
+      end
+
+      def build_response(objects)
+        selected_objects = select_existing_objects(objects)
+
+        upload_hypermedia(objects, selected_objects)
+      end
+
+      def download_hypermedia(oid)
+        {
+         '_links' => {
+           'download' =>
+             {
+              'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{oid}",
+              'header' => {
+                'Accept' => "application/vnd.git-lfs+json; charset=utf-8",
+                'Authorization' => @env['HTTP_AUTHORIZATION']
+              }.compact
+            }
+          }
+        }
+      end
+
+      def upload_hypermedia(all_objects, existing_objects)
+        all_objects.each do |object|
+          object['_links'] = hypermedia_links(object) unless existing_objects.include?(object['oid'])
+        end
+
+        { 'objects' => all_objects }
+      end
+
+      def hypermedia_links(object)
+        {
+          "upload" => {
+            'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}",
+            'header' => { 'Authorization' => @env['HTTP_AUTHORIZATION'] }
+          }.compact
+        }
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4809e8349847398f6ca13ceaf3ccaf72f7c6d24d
--- /dev/null
+++ b/lib/gitlab/lfs/router.rb
@@ -0,0 +1,95 @@
+module Gitlab
+  module Lfs
+    class Router
+      def initialize(project, user, request)
+        @project = project
+        @user = user
+        @env = request.env
+        @request = request
+      end
+
+      def try_call
+        return unless @request && @request.path.present?
+
+        case @request.request_method
+        when 'GET'
+          get_response
+        when 'POST'
+          post_response
+        when 'PUT'
+          put_response
+        else
+          nil
+        end
+      end
+
+      private
+
+      def get_response
+        path_match = @request.path.match(/\/(info\/lfs|gitlab-lfs)\/objects\/([0-9a-f]{64})$/)
+        return nil unless path_match
+
+        oid = path_match[2]
+        return nil unless oid
+
+        case path_match[1]
+        when "info/lfs"
+          lfs.render_download_hypermedia_response(oid)
+        when "gitlab-lfs"
+          lfs.render_download_object_response(oid)
+        else
+          nil
+        end
+      end
+
+      def post_response
+        post_path = @request.path.match(/\/info\/lfs\/objects(\/batch)?$/)
+        return nil unless post_path
+
+        # Check for Batch API
+        if post_path[0].ends_with?("/info/lfs/objects/batch")
+          lfs.render_lfs_api_auth
+        else
+          nil
+        end
+      end
+
+      def put_response
+        object_match = @request.path.match(/\/gitlab-lfs\/objects\/([0-9a-f]{64})\/([0-9]+)(|\/authorize){1}$/)
+        return nil if object_match.nil?
+
+        oid = object_match[1]
+        size = object_match[2].try(:to_i)
+        return nil if oid.nil? || size.nil?
+
+        # GitLab-workhorse requests
+        # 1. Try to authorize the request
+        # 2. send a request with a header containing the name of the temporary file
+        if object_match[3] && object_match[3] == '/authorize'
+          lfs.render_storage_upload_authorize_response(oid, size)
+        else
+          tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP'])
+          return nil unless tmp_file_name
+
+          lfs.render_storage_upload_store_response(oid, size, tmp_file_name)
+        end
+      end
+
+      def lfs
+        return unless @project
+
+        Gitlab::Lfs::Response.new(@project, @user, @request)
+      end
+
+      def sanitize_tmp_filename(name)
+        if name.present?
+          name.gsub!(/^.*(\\|\/)/, '')
+          name = name.match(/[0-9a-f]{73}/)
+          name[0] if name
+        else
+          nil
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index 32a368c2e2beda201bb63eb4b28251eda26bb390..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'
@@ -50,26 +86,20 @@ module Gitlab
     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)
 
@@ -78,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
 
@@ -130,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
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/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb
index 6ee3d1ce03903465b9bc694ab26cedb5271c630a..632be4d754255bbf4803dbe3c6f2e12ace6a4b9a 100644
--- a/lib/gitlab/markdown/relative_link_filter.rb
+++ b/lib/gitlab/markdown/relative_link_filter.rb
@@ -51,7 +51,7 @@ module Gitlab
           relative_url_root,
           context[:project].path_with_namespace,
           path_type(file_path),
-          ref || 'master',  # assume that if no ref exists we can point to master
+          ref || context[:project].default_branch,  # if no ref exists, point to the default branch
           file_path
         ].compact.join('/').squeeze('/').chomp('/')
 
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/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 30497e274c251bb02aa9d53e293e018ba871b5ec..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
 
@@ -41,21 +42,32 @@ module Gitlab
     def pipeline_result(filter_type)
       return [] if @text.blank?
       
-      klass  = filter_type.to_s.camelize + 'ReferenceFilter'
+      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/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 1e55c5a04868be641c30c196b09230e6682377cf..93f2ad07aeb2e5e3ddfdb0663e015a0a37cccf4b 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -38,13 +38,13 @@ 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
 server {
-  ## Either remove "default_server" from the listen line below, 
+  ## Either remove "default_server" from the listen line below,
   ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
   ## to be served if you visit any address that your server responds to, eg.
   ## the ip address of the server (http://x.x.x.x/)n 0.0.0.0:80 default_server;
@@ -113,25 +113,48 @@ server {
     proxy_pass http://gitlab;
   }
 
+  location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects {
+    client_max_body_size 0;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
   location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
-    # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block
-    error_page 418 = @gitlab-git-http-server;
+    # '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-git-http-server block
-    error_page 418 = @gitlab-git-http-server;
+    # '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-git-http-server block
-    error_page 418 = @gitlab-git-http-server;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
     return 418;
   }
 
-  location @gitlab-git-http-server {
+  # Build artifacts should be submitted to this location
+  location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
+      client_max_body_size 0;
+      # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+      error_page 418 = @gitlab-workhorse;
+      return 418;
+  }
+
+  # Build artifacts should be submitted to this location
+  location ~ /ci/api/v1/builds/[0-9]+/artifacts {
+      client_max_body_size 0;
+      # '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;
@@ -147,7 +170,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;
 
@@ -156,7 +179,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 08641bbcc178f4c5ede2559b4d7e781f7ccd9395..90749947fa4f08ebcbee58d1fd9c53b0f5cb6a7b 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -42,13 +42,13 @@ 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
 server {
-  ## Either remove "default_server" from the listen line below, 
+  ## Either remove "default_server" from the listen line below,
   ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
   ## to be served if you visit any address that your server responds to, eg.
   ## the ip address of the server (http://x.x.x.x/)
@@ -160,25 +160,48 @@ server {
     proxy_pass http://gitlab;
   }
 
+  location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects {
+    client_max_body_size 0;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
   location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
-    # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block
-    error_page 418 = @gitlab-git-http-server;
+    # '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-git-http-server block
-    error_page 418 = @gitlab-git-http-server;
+    # '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-git-http-server block
-    error_page 418 = @gitlab-git-http-server;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
     return 418;
   }
 
-  location @gitlab-git-http-server {
+  # Build artifacts should be submitted to this location
+  location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
+      client_max_body_size 0;
+      # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+      error_page 418 = @gitlab-workhorse;
+      return 418;
+  }
+
+  # Build artifacts should be submitted to this location
+  location ~ /ci/api/v1/builds/[0-9]+/artifacts {
+      client_max_body_size 0;
+      # '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;
@@ -194,7 +217,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;
 
@@ -203,7 +226,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/flay.rake b/lib/tasks/flay.rake
new file mode 100644
index 0000000000000000000000000000000000000000..dfb9df4772aeb2650e5aac77176252a6fd678543
--- /dev/null
+++ b/lib/tasks/flay.rake
@@ -0,0 +1,9 @@
+desc 'Code duplication analyze via flay'
+task :flay do
+  output = %x(bundle exec flay --mass 30 app/ lib/gitlab/)
+
+  if output.include? "Similar code found"
+    puts output
+    exit 1
+  end
+end
diff --git a/lib/tasks/flog.rake b/lib/tasks/flog.rake
new file mode 100644
index 0000000000000000000000000000000000000000..3bfe999ae74d4020c64ebe7b36515d561ff3a178
--- /dev/null
+++ b/lib/tasks/flog.rake
@@ -0,0 +1,25 @@
+desc 'Code complexity analyze via flog'
+task :flog do
+  output = %x(bundle exec flog -m app/ lib/gitlab)
+  exit_code = 0
+  minimum_score = 70
+  output = output.lines
+
+  # Skip total complexity score
+  output.shift
+
+  # Skip some trash info
+  output.shift
+
+  output.each do |line|
+    score, method = line.split(" ")
+    score = score.to_i
+
+    if score > minimum_score
+      exit_code = 1
+      puts "High complexity in #{method}. Score: #{score}"
+    end
+  end
+
+  exit exit_code
+end
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index f20c7f71ba5a429d07901f8f930ad8f81ab5f414..3c46bcea40edd172754113d792537820d8f2ce06 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -12,6 +12,7 @@ namespace :gitlab do
       Rake::Task["gitlab:backup:repo:create"].invoke
       Rake::Task["gitlab:backup:uploads:create"].invoke
       Rake::Task["gitlab:backup:builds:create"].invoke
+      Rake::Task["gitlab:backup:artifacts:create"].invoke
 
       backup = Backup::Manager.new
       backup.pack
@@ -32,6 +33,7 @@ namespace :gitlab do
       Rake::Task["gitlab:backup:repo:restore"].invoke unless backup.skipped?("repositories")
       Rake::Task["gitlab:backup:uploads:restore"].invoke unless backup.skipped?("uploads")
       Rake::Task["gitlab:backup:builds:restore"].invoke unless backup.skipped?("builds")
+      Rake::Task["gitlab:backup:artifacts:restore"].invoke unless backup.skipped?("artifacts")
       Rake::Task["gitlab:shell:setup"].invoke
 
       backup.cleanup
@@ -113,6 +115,25 @@ namespace :gitlab do
       end
     end
 
+    namespace :artifacts do
+      task create: :environment do
+        $progress.puts "Dumping artifacts ... ".blue
+
+        if ENV["SKIP"] && ENV["SKIP"].include?("artifacts")
+          $progress.puts "[SKIPPED]".cyan
+        else
+          Backup::Artifacts.new.dump
+          $progress.puts "done".green
+        end
+      end
+
+      task restore: :environment do
+        $progress.puts "Restoring artifacts ... ".blue
+        Backup::Artifacts.new.restore
+        $progress.puts "done".green
+      end
+    end
+
     def configure_cron_mode
       if ENV['CRON']
         # We need an object we can say 'puts' and 'print' to; let's use a
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 606bf241db796db02aec85c1f0b8a2ea65a82706..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
@@ -824,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/grape.rake b/lib/tasks/grape.rake
new file mode 100644
index 0000000000000000000000000000000000000000..9980e0b7984b6aee4a9e63544810839bd2b66498
--- /dev/null
+++ b/lib/tasks/grape.rake
@@ -0,0 +1,8 @@
+namespace :grape do
+  desc 'Print compiled grape routes'
+  task routes: :environment do
+    API::API.routes.each do |route|
+      puts route
+    end
+  end
+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/lib/uploaded_file.rb b/lib/uploaded_file.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d4291f012d342ac6e0d86d1d1b3d024ffd7f2bf7
--- /dev/null
+++ b/lib/uploaded_file.rb
@@ -0,0 +1,37 @@
+require "tempfile"
+require "fileutils"
+
+# Taken from: Rack::Test::UploadedFile
+class UploadedFile
+
+  # The filename, *not* including the path, of the "uploaded" file
+  attr_reader :original_filename
+
+  # The tempfile
+  attr_reader :tempfile
+
+  # The content type of the "uploaded" file
+  attr_accessor :content_type
+
+  def initialize(path, filename, content_type = "text/plain")
+    raise "#{path} file does not exist" unless ::File.exist?(path)
+
+    @content_type = content_type
+    @original_filename = filename || ::File.basename(path)
+    @tempfile = File.new(path, 'rb')
+  end
+
+  def path
+    @tempfile.path
+  end
+
+  alias_method :local_path, :path
+
+  def method_missing(method_name, *args, &block) #:nodoc:
+    @tempfile.__send__(method_name, *args, &block)
+  end
+
+  def respond_to?(method_name, include_private = false) #:nodoc:
+    @tempfile.respond_to?(method_name, include_private) || super
+  end
+end
diff --git a/shared/.gitkeep b/shared/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/shared/artifacts/.gitkeep b/shared/artifacts/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/shared/artifacts/tmp/cache/.gitkeep b/shared/artifacts/tmp/cache/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/shared/artifacts/tmp/uploads/.gitkeep b/shared/artifacts/tmp/uploads/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
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/user_spec.rb b/spec/benchmarks/models/user_spec.rb
index cc5c3904193930ab407e6ab4d8e3280058ad2d41..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|
@@ -39,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..8b7af4d3a0aa94542ab494ca663693142ac4b409 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -7,21 +7,6 @@ describe Admin::UsersController do
     sign_in(admin)
   end
 
-  describe 'POST login_as' do
-    let(:user) { create(:user) }
-
-    it 'logs admin as another user' do
-      expect(warden.authenticate(scope: :user)).not_to eq(user)
-      post :login_as, id: user.username
-      expect(warden.authenticate(scope: :user)).to eq(user)
-    end
-
-    it 'redirects user to homepage' do
-      post :login_as, id: user.username
-      expect(response).to redirect_to(root_path)
-    end
-  end
-
   describe 'DELETE #user with projects' do
     let(:user) { create(:user) }
     let(:project) { create(:project, namespace: user.namespace) }
@@ -37,6 +22,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/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index 2a447248b704d42a706c5b8940597988d4d87cba..be19f1abc534bf7c38435e748d5113f07685e680 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -23,6 +23,22 @@ describe Projects::CompareController do
     expect(assigns(:commits).length).to be >= 1
   end
 
+  it 'compare should show some diffs with ignore whitespace change option' do
+    get(:show,
+        namespace_id: project.namespace.to_param,
+        project_id: project.to_param,
+        from: '08f22f25',
+        to: '66eceea0',
+        w: 1)
+
+    expect(response).to be_success
+    expect(assigns(:diffs).length).to be >= 1
+    expect(assigns(:commits).length).to be >= 1
+    # without whitespace option, there are more than 2 diff_splits
+    diff_splits = assigns(:diffs)[0].diff.split("\n")
+    expect(diff_splits.length).to be <= 2
+  end
+
   describe 'non-existent refs' do
     it 'invalid source ref' do
       get(:show,
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index b8db8591709b5932abadd5ed98f7b6ba909f1348..3e5e1fa87ae710b80cbddc2c40d63e7e8ecb56af 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -147,6 +147,34 @@ describe Projects::MergeRequestsController do
     end
   end
 
+  describe 'GET diffs with ignore_whitespace_change' do
+    def go(format: 'html')
+      get :diffs,
+          namespace_id: project.namespace.to_param,
+          project_id: project.to_param,
+          id: merge_request.iid,
+          format: format,
+          w: 1
+    end
+
+    context 'as html' do
+      it 'renders the diff template' do
+        go
+
+        expect(response).to render_template('diffs')
+      end
+    end
+    
+    context 'as json' do
+      it 'renders the diffs template to a string' do
+        go format: 'json'
+
+        expect(response).to render_template('projects/merge_requests/show/_diffs')
+        expect(JSON.parse(response.body)).to have_key('html')
+      end
+    end
+  end
+
   describe 'GET commits' do
     def go(format: 'html')
       get :commits,
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_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..11cb8c9eeaa25647707ff80364b7b8f9a4c7730d 100644
--- a/spec/factories/ci/projects.rb
+++ b/spec/factories/ci/projects.rb
@@ -31,14 +31,20 @@ FactoryGirl.define do
   factory :ci_project_without_token, class: Ci::Project do
     default_ref 'master'
 
-    gl_project factory: :empty_project
+    shared_runners_enabled false
 
     factory :ci_project do
       token 'iPWx6WM4lhHNedGfBpPJNP'
     end
 
-    factory :ci_public_project do
-      public true
+    initialize_with do
+      # TODO:
+      # this is required, because builds_enabled is initialized when Project is created
+      # and this create gitlab_ci_project if builds is set to true
+      # here we take created gitlab_ci_project and update it's attributes
+      ci_project = create(:empty_project).ensure_gitlab_ci_project
+      ci_project.update_attributes(attributes)
+      ci_project
     end
   end
 end
diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb
index 6829387c660c86490e08d806fe90f315a8ee29f1..8b12ee11af5642eeb41153a733efc1188a588169 100644
--- a/spec/factories/labels.rb
+++ b/spec/factories/labels.rb
@@ -8,6 +8,7 @@
 #  project_id :integer
 #  created_at :datetime
 #  updated_at :datetime
+#  template   :boolean          default(FALSE)
 #
 
 # Read about factories at https://github.com/thoughtbot/factory_girl
diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7fb2d77ca3201574b83cf8d8c6a70a5b4c507661
--- /dev/null
+++ b/spec/factories/lfs_objects.rb
@@ -0,0 +1,12 @@
+# Read about factories at https://github.com/thoughtbot/factory_girl
+
+FactoryGirl.define do
+  factory :lfs_object do
+    oid "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80"
+    size 499013
+  end
+
+  trait :with_file do
+    file { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") }
+  end
+end
diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..93de6607df84a5fbfc4f977b56aabea1fcaec288
--- /dev/null
+++ b/spec/factories/lfs_objects_projects.rb
@@ -0,0 +1,8 @@
+# Read about factories at https://github.com/thoughtbot/factory_girl
+
+FactoryGirl.define do
+  factory :lfs_objects_project do
+    lfs_object
+    project
+  end
+end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 6080d0ccdefcc10e3b599372a1ab02ef4abec480..729a49c9f7215326425e75a4649323fa480b7ff9 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -20,6 +20,7 @@
 #  position          :integer          default(0)
 #  locked_at         :datetime
 #  updated_by_id     :integer
+#  merge_error       :string(255)
 #
 
 FactoryGirl.define do
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
new file mode 100644
index 0000000000000000000000000000000000000000..43d09b175347046eaa599c58a0550295874863a8
--- /dev/null
+++ b/spec/factories/releases.rb
@@ -0,0 +1,21 @@
+# == Schema Information
+#
+# Table name: releases
+#
+#  id          :integer          not null, primary key
+#  tag         :string(255)
+#  description :text
+#  project_id  :integer
+#  created_at  :datetime
+#  updated_at  :datetime
+#
+
+# 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/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index c2c7364f6c51b59dd9d75182f2d3b85a3577ccb4..4c756a8e73230191cfdd89f14ebb710383d5aef0 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -111,24 +111,50 @@ describe "Admin::Users", feature: true  do
       expect(page).to have_content(@user.name)
     end
 
-    describe 'Login as another user' do
-      it 'should show login button for other users and check that it works' do
-        another_user = create(:user)
+    describe 'Impersonation' do
+      let(:another_user) { create(:user) }
+      before { visit admin_user_path(another_user) }
 
-        visit admin_user_path(another_user)
-
-        click_link 'Log in as this user'
+      context 'before impersonating' do
+        it 'shows impersonate button for other users' do
+          expect(page).to have_content('Impersonate')
+        end
 
-        expect(page).to have_content("Logged in as #{another_user.username}")
+        it 'should not show impersonate button for admin itself' do
+          visit admin_user_path(@user)
 
-        page.within '.sidebar-user .username' do
-          expect(page).to have_content(another_user.username)
+          expect(page).not_to have_content('Impersonate')
         end
       end
 
-      it 'should not show login button for admin itself' do
-        visit admin_user_path(@user)
-        expect(page).not_to have_content('Log in as this user')
+      context 'when impersonating' do
+        before { click_link 'Impersonate' }
+
+        it 'logs in as the user when impersonate is clicked' do
+          page.within '.sidebar-user .username' do
+            expect(page).to have_content(another_user.username)
+          end
+        end
+
+        it 'sees impersonation log out icon' do
+          icon = first('.fa.fa-user-secret')
+
+          expect(icon).to_not eql nil
+        end
+
+        it 'can log out of impersonated user back to original user' do
+          find(:css, 'li.impersonation a').click
+
+          page.within '.sidebar-user .username' do
+            expect(page).to have_content(@user.username)
+          end
+        end
+
+        it 'is redirected back to the impersonated users page in the admin after stopping' do
+          find(:css, 'li.impersonation a').click
+
+          expect(current_path).to eql "/admin/users/#{another_user.username}"
+        end
       end
     end
 
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 154857e77fef6f7d5e89238a81107134c1b4f1d9..5213ce1099fd0521bfffeac30ce81050aea5c985 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -1,6 +1,8 @@
 require 'spec_helper'
 
 describe "Builds" do
+  let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+
   before do
     login_as(:user)
     @commit = FactoryGirl.create :ci_commit
@@ -47,10 +49,11 @@ describe "Builds" do
     end
   end
 
-  describe "GET /:project/builds/:id/cancel_all" do
+  describe "POST /:project/builds/:id/cancel_all" do
     before do
       @build.run!
-      visit cancel_all_namespace_project_builds_path(@gl_project.namespace, @gl_project)
+      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' }
@@ -65,12 +68,22 @@ describe "Builds" do
     it { expect(page).to have_content @commit.sha[0..7] }
     it { expect(page).to have_content @commit.git_commit_message }
     it { expect(page).to have_content @commit.git_author_name }
+
+    context "Download artifacts" do
+      before do
+        @build.update_attributes(artifacts_file: artifacts_file)
+        visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+      end
+
+      it { expect(page).to have_content 'Download artifacts' }
+    end
   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' }
@@ -79,11 +92,23 @@ 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
 
     it { expect(page).to have_content 'pending' }
     it { expect(page).to have_content 'Cancel' }
   end
+
+  describe "GET /:project/builds/:id/download" do
+    before do
+      @build.update_attributes(artifacts_file: artifacts_file)
+      visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+      click_link 'Download artifacts'
+    end
+
+    it { expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) }
+  end
 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..90739cd6a28f1fdece82ca309f1d1c429e8db2e9 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -19,7 +19,7 @@ describe "Commits" do
       stub_ci_commit_to_return_yaml_file
     end
 
-    describe "GET /:project/commits/:sha" do
+    describe "GET /:project/commits/:sha/ci" do
       before do
         visit ci_status_path(@commit)
       end
@@ -29,10 +29,24 @@ describe "Commits" do
       it { expect(page).to have_content @commit.git_author_name }
     end
 
+    context "Download artifacts" do
+      let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+
+      before do
+        @build.update_attributes(artifacts_file: artifacts_file)
+      end
+
+      it do
+        visit ci_status_path(@commit)
+        click_on "Download artifacts"
+        expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type)
+      end
+    end
+
     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/features/runners_spec.rb b/spec/features/runners_spec.rb
index 06adb7633b28b0ae7e63dbfd9f292a76ab19c07c..b025902663068080afd7cc28ed6a320ca979c96f 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -14,15 +14,25 @@ describe "Runners" do
       @project2 = FactoryGirl.create :ci_project
       @project2.gl_project.team << [user, :master]
 
+      @project3 = FactoryGirl.create :ci_project
+      @project3.gl_project.team << [user, :developer]
+
       @shared_runner = FactoryGirl.create :ci_shared_runner
       @specific_runner = FactoryGirl.create :ci_specific_runner
       @specific_runner2 = FactoryGirl.create :ci_specific_runner
+      @specific_runner3 = FactoryGirl.create :ci_specific_runner
       @project.runners << @specific_runner
       @project2.runners << @specific_runner2
+      @project3.runners << @specific_runner3
 
       visit runners_path(@project.gl_project)
     end
 
+    before do
+      expect(page).to_not have_content(@specific_runner3.display_name)
+      expect(page).to_not have_content(@specific_runner3.display_name)
+    end
+
     it "places runners in right places" do
       expect(page.find(".available-specific-runners")).to have_content(@specific_runner2.display_name)
       expect(page.find(".activated-specific-runners")).to have_content(@specific_runner.display_name)
@@ -76,10 +86,10 @@ describe "Runners" do
       @project.gl_project.team << [user, :master]
       @specific_runner = FactoryGirl.create :ci_specific_runner
       @project.runners << @specific_runner
-      visit runners_path(@project.gl_project)
     end
 
     it "shows runner information" do
+      visit runners_path(@project.gl_project)
       click_on @specific_runner.short_sha
       expect(page).to have_content(@specific_runner.platform)
     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/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/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..7d90f9877c6c6f52489774a69797f380c042ffa4 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)
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+          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)
+
+          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
+
+        it "returns builds if except has regexp with another branch" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", except: ["/^deploy$/"] }
+                             })
 
-        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)
+          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,117 @@ 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 "Caches" do
+      it "returns cache when defined globally" do
+        config = YAML.dump({
+                             cache: { paths: ["logs/", "binaries/"], untracked: true },
+                             rspec: {
+                               script: "rspec"
+                             }
+                           })
+
+        config_processor = GitlabCiYamlProcessor.new(config)
+
+        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[:options][:cache]).to eq(
+          paths: ["logs/", "binaries/"],
+          untracked: true,
+        )
+      end
+
+      it "returns cache when defined in a job" do
+        config = YAML.dump({
+                             rspec: {
+                               cache: { paths: ["logs/", "binaries/"], untracked: true },
+                               script: "rspec"
+                             }
+                           })
+
+        config_processor = GitlabCiYamlProcessor.new(config)
+
+        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[:options][:cache]).to eq(
+          paths: ["logs/", "binaries/"],
+          untracked: true,
+        )
+      end
+
+      it "overwrite cache when defined for a job and globally" do
+        config = YAML.dump({
+                             cache: { paths: ["logs/", "binaries/"], untracked: true },
+                             rspec: {
+                               script: "rspec",
+                               cache: { paths: ["test/"], untracked: false },
+                             }
+                           })
+
+        config_processor = GitlabCiYamlProcessor.new(config)
+
+        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[:options][:cache]).to eq(
+          paths: ["test/"],
+          untracked: false,
+        )
+      end
+    end
+
+    describe "Artifacts" do
+      it "returns artifacts when defined" do
+        config = YAML.dump({
+                             image:         "ruby:2.1",
+                             services:      ["mysql"],
+                             before_script: ["pwd"],
+                             rspec:         {
+                               artifacts: { paths: ["logs/", "binaries/"], untracked: true },
+                               script: "rspec"
+                             }
+                           })
+
+        config_processor = GitlabCiYamlProcessor.new(config)
+
+        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({
+          except: nil,
+          stage: "test",
+          stage_idx: 1,
+          name: :rspec,
+          only: nil,
+          commands: "pwd\nrspec",
+          tag_list: [],
+          options: {
+            image: "ruby:2.1",
+            services: ["mysql"],
+            artifacts: {
+              paths: ["logs/", "binaries/"],
+              untracked: true
+            }
+          },
+          when: "on_success",
+          allow_failure: false
+        })
+      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 +432,198 @@ 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
+
+      it "returns errors if job artifacts:untracked is not an array of strings" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { untracked: "string" } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:untracked parameter should be an boolean")
+      end
+
+      it "returns errors if job artifacts:paths is not an array of strings" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { paths: "string" } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:paths parameter should be an array of strings")
+      end
+
+      it "returns errors if cache:untracked is not an array of strings" do
+        config = YAML.dump({ cache: { untracked: "string" }, rspec: { script: "test" } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:untracked parameter should be an boolean")
+      end
+
+      it "returns errors if cache:paths is not an array of strings" do
+        config = YAML.dump({ cache: { paths: "string" }, rspec: { script: "test" } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:paths parameter should be an array of strings")
+      end
+
+      it "returns errors if job cache:untracked is not an array of strings" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { untracked: "string" } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:untracked parameter should be an boolean")
+      end
+
+      it "returns errors if job cache:paths is not an array of strings" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { paths: "string" } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:paths parameter should be an array of strings")
+      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/diff/inline_diff_spec.rb b/spec/lib/gitlab/inline_diff_spec.rb
similarity index 100%
rename from spec/lib/gitlab/diff/inline_diff_spec.rb
rename to spec/lib/gitlab/inline_diff_spec.rb
diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cebcb5bc8871a4cd8f34d2e0214da4afa2d59c3c
--- /dev/null
+++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb
@@ -0,0 +1,650 @@
+require 'spec_helper'
+
+describe Gitlab::Lfs::Router do
+  let(:project) { create(:project) }
+  let(:public_project) { create(:project, :public) }
+  let(:forked_project) { fork_project(public_project, user) }
+
+  let(:user) { create(:user) }
+  let(:user_two) { create(:user) }
+  let!(:lfs_object) { create(:lfs_object, :with_file) }
+
+  let(:request) { Rack::Request.new(env) }
+  let(:env) do
+    {
+      'rack.input'     => '',
+      'REQUEST_METHOD' => 'GET',
+    }
+  end
+
+  let(:lfs_router_auth) { new_lfs_router(project, user) }
+  let(:lfs_router_noauth) { new_lfs_router(project, nil) }
+  let(:lfs_router_public_auth) { new_lfs_router(public_project, user) }
+  let(:lfs_router_public_noauth) { new_lfs_router(public_project, nil) }
+  let(:lfs_router_forked_noauth) { new_lfs_router(forked_project, nil) }
+  let(:lfs_router_forked_auth) { new_lfs_router(forked_project, user_two) }
+
+  let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" }
+  let(:sample_size) { 499013 }
+
+  describe 'when lfs is disabled' do
+    before do
+      allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
+      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
+    end
+
+    it 'responds with 501' do
+      respond_with_disabled = [ 501,
+                                { "Content-Type"=>"application/vnd.git-lfs+json" },
+                                ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]
+                              ]
+      expect(lfs_router_auth.try_call).to match_array(respond_with_disabled)
+    end
+  end
+
+  describe 'when fetching lfs object' do
+    before do
+      enable_lfs
+      env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8"
+      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
+    end
+
+    describe 'when user is authenticated' do
+      context 'and user has project download access' do
+        before do
+          @auth = authorize(user)
+          env["HTTP_AUTHORIZATION"] = @auth
+          project.lfs_objects << lfs_object
+          project.team << [user, :master]
+        end
+
+        it "responds with status 200" do
+          expect(lfs_router_auth.try_call.first).to eq(200)
+        end
+
+        it "responds with download hypermedia" do
+          json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first)
+
+          expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}")
+          expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth, "Accept" => "application/vnd.git-lfs+json; charset=utf-8")
+        end
+      end
+
+      context 'and user does not have project access' do
+        it "responds with status 403" do
+          expect(lfs_router_auth.try_call.first).to eq(403)
+        end
+      end
+    end
+
+    describe 'when user is unauthenticated' do
+      context 'and user does not have download access' do
+        it "responds with status 401" do
+          expect(lfs_router_noauth.try_call.first).to eq(401)
+        end
+      end
+
+      context 'and user has download access' do
+        before do
+          project.team << [user, :master]
+        end
+
+        it "responds with status 401" do
+          expect(lfs_router_noauth.try_call.first).to eq(401)
+        end
+      end
+    end
+
+    describe 'and project is public' do
+      context 'and project has access to the lfs object' do
+        before do
+          public_project.lfs_objects << lfs_object
+        end
+
+        context 'and user is authenticated' do
+          it "responds with status 200 and sends download hypermedia" do
+            expect(lfs_router_public_auth.try_call.first).to eq(200)
+            json_response = ActiveSupport::JSON.decode(lfs_router_public_auth.try_call.last.first)
+
+            expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}")
+            expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8")
+          end
+        end
+
+        context 'and user is unauthenticated' do
+          it "responds with status 200 and sends download hypermedia" do
+            expect(lfs_router_public_noauth.try_call.first).to eq(200)
+            json_response = ActiveSupport::JSON.decode(lfs_router_public_noauth.try_call.last.first)
+
+            expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}")
+            expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8")
+          end
+        end
+      end
+
+      context 'and project does not have access to the lfs object' do
+        it "responds with status 404" do
+          expect(lfs_router_public_auth.try_call.first).to eq(404)
+        end
+      end
+    end
+
+    describe 'and request comes from gitlab-workhorse' do
+      before do
+        env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}"
+      end
+      context 'without user being authorized' do
+        it "responds with status 401" do
+          expect(lfs_router_noauth.try_call.first).to eq(401)
+        end
+      end
+
+      context 'with required headers' do
+        before do
+          env['HTTP_X_SENDFILE_TYPE'] = "X-Sendfile"
+        end
+
+        context 'when user does not have project access' do
+          it "responds with status 403" do
+            expect(lfs_router_auth.try_call.first).to eq(403)
+          end
+        end
+
+        context 'when user has project access' do
+          before do
+            project.lfs_objects << lfs_object
+            project.team << [user, :master]
+          end
+
+          it "responds with status 200" do
+            expect(lfs_router_auth.try_call.first).to eq(200)
+          end
+
+          it "responds with the file location" do
+            expect(lfs_router_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
+            expect(lfs_router_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
+          end
+        end
+      end
+
+      context 'without required headers' do
+        it "responds with status 403" do
+          expect(lfs_router_auth.try_call.first).to eq(403)
+        end
+      end
+    end
+
+    describe 'from a forked public project' do
+      before do
+        env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8"
+        env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
+      end
+
+      context "when fetching a lfs object" do
+        context "and user has project download access" do
+          before do
+            public_project.lfs_objects << lfs_object
+          end
+
+          it "can download the lfs object" do
+            expect(lfs_router_forked_auth.try_call.first).to eq(200)
+            json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first)
+
+            expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}")
+            expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8")
+          end
+        end
+
+        context "and user is not authenticated but project is public" do
+          before do
+            public_project.lfs_objects << lfs_object
+          end
+
+          it "can download the lfs object" do
+            expect(lfs_router_forked_auth.try_call.first).to eq(200)
+          end
+        end
+
+        context "and user has project download access" do
+          before do
+            env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897"
+            @auth = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
+            env["HTTP_AUTHORIZATION"] = @auth
+            lfs_object_two = create(:lfs_object, :with_file, oid: "91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", size: 1575078)
+            public_project.lfs_objects << lfs_object_two
+          end
+
+          it "can get a lfs object that is not in the forked project" do
+            expect(lfs_router_forked_auth.try_call.first).to eq(200)
+
+            json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first)
+            expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
+            expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8", "Authorization" => @auth)
+          end
+        end
+
+        context "and user has project download access" do
+          before do
+            env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/267c8b1d876743971e3a9978405818ff5ca731c4c870b06507619cd9b1847b6b"
+            lfs_object_three = create(:lfs_object, :with_file, oid: "267c8b1d876743971e3a9978405818ff5ca731c4c870b06507619cd9b1847b6b", size: 127192524)
+            project.lfs_objects << lfs_object_three
+          end
+
+          it "cannot get a lfs object that is not in the project" do
+            expect(lfs_router_forked_auth.try_call.first).to eq(404)
+          end
+        end
+      end
+    end
+  end
+
+  describe 'when initiating pushing of the lfs object' do
+    before do
+      enable_lfs
+      env['REQUEST_METHOD'] = 'POST'
+      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
+    end
+
+    describe 'when user is authenticated' do
+      before do
+        body = { 'objects' => [{
+                   'oid' => sample_oid,
+                   'size' => sample_size
+                  }]
+                }.to_json
+        env['rack.input'] = StringIO.new(body)
+      end
+
+      describe 'when user has project push access' do
+        before do
+          @auth = authorize(user)
+          env["HTTP_AUTHORIZATION"] = @auth
+          project.team << [user, :master]
+        end
+
+        context 'when pushing an lfs object that already exists' do
+          before do
+            public_project.lfs_objects << lfs_object
+          end
+
+          it "responds with status 200 and links the object to the project" do
+            response_body = lfs_router_auth.try_call.last
+            response = ActiveSupport::JSON.decode(response_body.first)
+
+            expect(response['objects']).to be_kind_of(Array)
+            expect(response['objects'].first['oid']).to eq(sample_oid)
+            expect(response['objects'].first['size']).to eq(sample_size)
+            expect(lfs_object.projects.pluck(:id)).to_not include(project.id)
+            expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
+            expect(response['objects'].first).to have_key('_links')
+          end
+        end
+
+        context 'when pushing a lfs object that does not exist' do
+          before do
+            body = {
+              'objects' => [{
+                'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                'size' => 1575078
+                }]
+                }.to_json
+            env['rack.input'] = StringIO.new(body)
+          end
+
+          it "responds with status 200 and upload hypermedia link" do
+            response = lfs_router_auth.try_call
+            expect(response.first).to eq(200)
+
+            response_body = ActiveSupport::JSON.decode(response.last.first)
+            expect(response_body['objects']).to be_kind_of(Array)
+            expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
+            expect(response_body['objects'].first['size']).to eq(1575078)
+            expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
+            expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
+            expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth)
+          end
+        end
+
+        context 'when pushing one new and one existing lfs object' do
+          before do
+            body = {
+              'objects' => [
+                { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                  'size' => 1575078
+                },
+                { 'oid' => sample_oid,
+                  'size' => sample_size
+                }
+              ]
+            }.to_json
+            env['rack.input'] = StringIO.new(body)
+            public_project.lfs_objects << lfs_object
+          end
+
+          it "responds with status 200 with upload hypermedia link for the new object" do
+            response = lfs_router_auth.try_call
+            expect(response.first).to eq(200)
+
+            response_body = ActiveSupport::JSON.decode(response.last.first)
+            expect(response_body['objects']).to be_kind_of(Array)
+
+
+            expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
+            expect(response_body['objects'].first['size']).to eq(1575078)
+            expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
+            expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth)
+
+            expect(response_body['objects'].last['oid']).to eq(sample_oid)
+            expect(response_body['objects'].last['size']).to eq(sample_size)
+            expect(lfs_object.projects.pluck(:id)).to_not include(project.id)
+            expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
+            expect(response_body['objects'].last).to have_key('_links')
+          end
+        end
+      end
+
+      context 'when user does not have push access' do
+        it 'responds with 403' do
+          expect(lfs_router_auth.try_call.first).to eq(403)
+        end
+      end
+    end
+
+    context 'when user is not authenticated' do
+      context 'when user has push access' do
+        before do
+          project.team << [user, :master]
+        end
+
+        it "responds with status 401" do
+          expect(lfs_router_public_noauth.try_call.first).to eq(401)
+        end
+      end
+
+      context 'when user does not have push access' do
+        it "responds with status 401" do
+          expect(lfs_router_public_noauth.try_call.first).to eq(401)
+        end
+      end
+    end
+  end
+
+  describe 'when pushing a lfs object' do
+    before do
+      enable_lfs
+      env['REQUEST_METHOD'] = 'PUT'
+    end
+
+    describe 'to one project' do
+      describe 'when user has push access to the project' do
+        before do
+          project.team << [user, :master]
+        end
+
+        describe 'when user is authenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(project)
+            end
+
+            it 'responds with status 200, location of lfs store and object details' do
+              json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first)
+
+              expect(lfs_router_auth.try_call.first).to eq(200)
+              expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
+              expect(json_response['LfsOid']).to eq(sample_oid)
+              expect(json_response['LfsSize']).to eq(sample_size)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(project)
+            end
+
+            it 'responds with status 200 and lfs object is linked to the project' do
+              expect(lfs_router_auth.try_call.first).to eq(200)
+              expect(lfs_object.projects.pluck(:id)).to include(project.id)
+            end
+          end
+        end
+
+        describe 'when user is unauthenticated' do
+          let(:lfs_router_noauth) { new_lfs_router(project, nil) }
+
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(project)
+            end
+
+            it 'responds with status 401' do
+              expect(lfs_router_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(project)
+            end
+
+            it 'responds with status 401' do
+              expect(lfs_router_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent with a malformed headers' do
+            before do
+              env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
+              env["HTTP_X_GITLAB_LFS_TMP"] = "cat /etc/passwd"
+            end
+
+            it 'does not recognize it as a valid lfs command' do
+              expect(lfs_router_noauth.try_call).to eq(nil)
+            end
+          end
+        end
+      end
+
+      describe 'and user does not have push access' do
+        describe 'when user is authenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(project)
+            end
+
+            it 'responds with 403' do
+              expect(lfs_router_auth.try_call.first).to eq(403)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(project)
+            end
+
+            it 'responds with 403' do
+              expect(lfs_router_auth.try_call.first).to eq(403)
+            end
+          end
+        end
+
+        describe 'when user is unauthenticated' do
+          let(:lfs_router_noauth) { new_lfs_router(project, nil) }
+
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(project)
+            end
+
+            it 'responds with 401' do
+              expect(lfs_router_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(project)
+            end
+
+            it 'responds with 401' do
+              expect(lfs_router_noauth.try_call.first).to eq(401)
+            end
+          end
+        end
+      end
+    end
+
+    describe "to a forked project" do
+      let(:forked_project) { fork_project(public_project, user) }
+
+      describe 'when user has push access to the project' do
+        before do
+          forked_project.team << [user_two, :master]
+        end
+
+        describe 'when user is authenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(forked_project)
+            end
+
+            it 'responds with status 200, location of lfs store and object details' do
+              json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first)
+
+              expect(lfs_router_forked_auth.try_call.first).to eq(200)
+              expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
+              expect(json_response['LfsOid']).to eq(sample_oid)
+              expect(json_response['LfsSize']).to eq(sample_size)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(forked_project)
+            end
+
+            it 'responds with status 200 and lfs object is linked to the source project' do
+              expect(lfs_router_forked_auth.try_call.first).to eq(200)
+              expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
+            end
+          end
+        end
+
+        describe 'when user is unauthenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(forked_project)
+            end
+
+            it 'responds with status 401' do
+              expect(lfs_router_forked_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(forked_project)
+            end
+
+            it 'responds with status 401' do
+              expect(lfs_router_forked_noauth.try_call.first).to eq(401)
+            end
+          end
+        end
+      end
+
+      describe 'and user does not have push access' do
+        describe 'when user is authenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(forked_project)
+            end
+
+            it 'responds with 403' do
+              expect(lfs_router_forked_auth.try_call.first).to eq(403)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(forked_project)
+            end
+
+            it 'responds with 403' do
+              expect(lfs_router_forked_auth.try_call.first).to eq(403)
+            end
+          end
+        end
+
+        describe 'when user is unauthenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(forked_project)
+            end
+
+            it 'responds with 401' do
+              expect(lfs_router_forked_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(forked_project)
+            end
+
+            it 'responds with 401' do
+              expect(lfs_router_forked_noauth.try_call.first).to eq(401)
+            end
+          end
+        end
+      end
+
+      describe 'and second project not related to fork or a source project' do
+        let(:second_project) { create(:project) }
+        let(:lfs_router_second_project) { new_lfs_router(second_project, user) }
+
+        before do
+          public_project.lfs_objects << lfs_object
+          headers_for_upload_finalize(second_project)
+        end
+
+        context 'when pushing the same lfs object to the second project' do
+          before do
+            second_project.team << [user, :master]
+          end
+
+          it 'responds with 200 and links the lfs object to the project' do
+            expect(lfs_router_second_project.try_call.first).to eq(200)
+            expect(lfs_object.projects.pluck(:id)).to include(second_project.id, public_project.id)
+          end
+        end
+      end
+    end
+  end
+
+  def enable_lfs
+    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+  end
+
+  def authorize(user)
+    ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
+  end
+
+  def new_lfs_router(project, user)
+    Gitlab::Lfs::Router.new(project, user, request)
+  end
+
+  def header_for_upload_authorize(project)
+    env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize"
+  end
+
+  def headers_for_upload_finalize(project)
+    env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
+    env["HTTP_X_GITLAB_LFS_TMP"] = "#{sample_oid}6e561c9d4"
+  end
+
+  def fork_project(project, user, object = nil)
+    allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
+    Projects::ForkService.new(project, user, {}).execute
+  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/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/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..dfbac7b4004600c782713ec5f115f385c1617eb3 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -23,16 +23,20 @@
 #  after_sign_out_path          :string(255)
 #  session_expire_delay         :integer          default(10080), not null
 #  import_sources               :text
+#  help_page_text               :text
+#  admin_notification_email     :string(255)
+#  shared_runners_enabled       :boolean          default(TRUE), not null
+#  max_artifacts_size           :integer          default(100), not null
 #
 
 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 +57,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 7f5abb83ac2298813e596c95468be60fc16dbfc8..839b4c6b16ea93502329d6704f91c2e02c3628f6 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -400,4 +400,19 @@ describe Ci::Build do
       end
     end
   end
+
+  describe :download_url do
+    subject { build.download_url }
+
+    it "should be nil if artifact doesn't exist" do
+      build.update_attributes(artifacts_file: nil)
+      is_expected.to be_nil
+    end
+
+    it 'should be nil if artifact exist' do
+      gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
+      build.update_attributes(artifacts_file: gif)
+      is_expected.to_not be_nil
+    end
+  end
 end
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index d1cecce5a6da482533d6572c98febf564709c75c..a13f6458cac7bfb960893214986642518247dca4 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -1,18 +1,19 @@
 # == Schema Information
 #
-# Table name: commits
+# Table name: ci_commits
 #
-#  id           :integer          not null, primary key
-#  project_id   :integer
-#  ref          :string(255)
-#  sha          :string(255)
-#  before_sha   :string(255)
-#  push_data    :text
-#  created_at   :datetime
-#  updated_at   :datetime
-#  tag          :boolean          default(FALSE)
-#  yaml_errors  :text
-#  committed_at :datetime
+#  id            :integer          not null, primary key
+#  project_id    :integer
+#  ref           :string(255)
+#  sha           :string(255)
+#  before_sha    :string(255)
+#  push_data     :text
+#  created_at    :datetime
+#  updated_at    :datetime
+#  tag           :boolean          default(FALSE)
+#  yaml_errors   :text
+#  committed_at  :datetime
+#  gl_project_id :integer
 #
 
 require 'spec_helper'
@@ -161,28 +162,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
@@ -194,12 +195,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
@@ -211,28 +212,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
@@ -242,7 +239,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)
@@ -250,6 +247,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
@@ -299,59 +419,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 490c6a679821b02ddd09c1b3c957cf0ef829ece1..ac7e38bbcb05dbfeea94325e345539e3baa0de20 100644
--- a/spec/models/ci/project_spec.rb
+++ b/spec/models/ci/project_spec.rb
@@ -1,9 +1,9 @@
 # == Schema Information
 #
-# Table name: projects
+# Table name: ci_projects
 #
 #  id                       :integer          not null, primary key
-#  name                     :string(255)      not null
+#  name                     :string(255)
 #  timeout                  :integer          default(3600), not null
 #  created_at               :datetime
 #  updated_at               :datetime
@@ -28,8 +28,8 @@
 require 'spec_helper'
 
 describe Ci::Project do
-  let(:gl_project) { FactoryGirl.create :empty_project }
-  let(:project) { FactoryGirl.create :ci_project, gl_project: gl_project }
+  let(:project) { FactoryGirl.create :ci_project }
+  let(:gl_project) { project.gl_project }
   subject { project }
 
   it { is_expected.to have_many(:runner_projects) }
@@ -194,18 +194,6 @@ describe Ci::Project do
     end
   end
 
-  describe 'Project.parse' do
-    let(:project) { FactoryGirl.create :project }
-
-    subject { Ci::Project.parse(project) }
-
-    it { is_expected.to be_valid }
-    it { is_expected.to be_kind_of(Ci::Project) }
-    it { expect(subject.name).to eq(project.name_with_namespace) }
-    it { expect(subject.gitlab_id).to eq(project.id) }
-    it { expect(subject.gitlab_url).to eq(project.web_url) }
-  end
-
   describe :repo_url_with_auth do
     let(:project) { FactoryGirl.create :ci_project }
     subject { project.repo_url_with_auth }
diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb
index 0218d48413008256d4ac34b9e789134e82be4533..37682c6ea0cc81489e8f6f3e4d4b06b51043ee63 100644
--- a/spec/models/ci/runner_project_spec.rb
+++ b/spec/models/ci/runner_project_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: runner_projects
+# Table name: ci_runner_projects
 #
 #  id         :integer          not null, primary key
 #  runner_id  :integer          not null
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index f8a51c29dc2c0216cd3187e2a5ec8514dd16902c..9a1233b909536c6daf629a10ea0f68d48f14c0c3 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: runners
+# Table name: ci_runners
 #
 #  id           :integer          not null, primary key
 #  token        :string(255)
diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb
index 2df70e888881a4777fcaff2274bf93f3706cce04..36cda988eb472fa79fc7312ecf77f2430a1e59f2 100644
--- a/spec/models/ci/service_spec.rb
+++ b/spec/models/ci/service_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index 19c14ef2da200cdf2c666d21499d58837c5f7a20..b8aa3c1e7775c468babbfb95cec25d59b85f3b82 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: ci_triggers
+#
+#  id         :integer          not null, primary key
+#  token      :string(255)
+#  project_id :integer          not null
+#  deleted_at :datetime
+#  created_at :datetime
+#  updated_at :datetime
+#
+
 require 'spec_helper'
 
 describe Ci::Trigger do
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index d034a6c7b9f550c13e1f485e4535a5135e1d57e4..a515f5881ff00860f60842eb04555bc3da534318 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: variables
+# Table name: ci_variables
 #
 #  id                   :integer          not null, primary key
 #  project_id           :integer          not null
diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb
index bf9481ab81d26c354eebd397229c95bae8bdf375..2865482a2120b35bc15654b48f3194e812654f57 100644
--- a/spec/models/ci/web_hook_spec.rb
+++ b/spec/models/ci/web_hook_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: web_hooks
+# Table name: ci_web_hooks
 #
 #  id         :integer          not null, primary key
 #  url        :string(255)      not null
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index c96a606fdaab60445d7415e85763049530672317..dca0715eed8f5a72419ed6711ad96bef406e55de 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+#  id                 :integer          not null, primary key
+#  project_id         :integer
+#  status             :string(255)
+#  finished_at        :datetime
+#  trace              :text
+#  created_at         :datetime
+#  updated_at         :datetime
+#  started_at         :datetime
+#  runner_id          :integer
+#  coverage           :float
+#  commit_id          :integer
+#  commands           :text
+#  job_id             :integer
+#  name               :string(255)
+#  deploy             :boolean          default(FALSE)
+#  options            :text
+#  allow_failure      :boolean          default(FALSE), not null
+#  stage              :string(255)
+#  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
+#
+
 require 'spec_helper'
 
 describe CommitStatus do
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/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index f442fa5fbe5f608b80f68dcddca4d215575e23b3..c86314c454c82f965f272b83f8aafc4cbb9e3e3d 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+#  id                 :integer          not null, primary key
+#  project_id         :integer
+#  status             :string(255)
+#  finished_at        :datetime
+#  trace              :text
+#  created_at         :datetime
+#  updated_at         :datetime
+#  started_at         :datetime
+#  runner_id          :integer
+#  coverage           :float
+#  commit_id          :integer
+#  commands           :text
+#  job_id             :integer
+#  name               :string(255)
+#  deploy             :boolean          default(FALSE)
+#  options            :text
+#  allow_failure      :boolean          default(FALSE), not null
+#  stage              :string(255)
+#  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
+#
+
 require 'spec_helper'
 
 describe GenericCommitStatus do
diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6eeff30b20e1ef88195ffed18991855a43ccddb5
--- /dev/null
+++ b/spec/models/global_milestone_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe GlobalMilestone do
+  let(:user) { create(:user) }
+  let(:user2) { create(:user) }
+  let(:group) { create(:group) }
+  let(:project1) { create(:project, group: group) }
+  let(:project2) { create(:project, path: 'gitlab-ci', group: group) }
+  let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) }
+  let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) }
+  let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) }
+  let(:milestone1_project3) { create(:milestone, title: "Milestone v1.2", project: project3) }
+  let(:milestone2_project1) { create(:milestone, title: "VD-123", project: project1) }
+  let(:milestone2_project2) { create(:milestone, title: "VD-123", project: project2) }
+  let(:milestone2_project3) { create(:milestone, title: "VD-123", project: project3) }
+
+  describe :build_collection do
+    before do
+      milestones =
+        [
+          milestone1_project1,
+          milestone1_project2,
+          milestone1_project3,
+          milestone2_project1,
+          milestone2_project2,
+          milestone2_project3
+        ]
+
+      @global_milestones = GlobalMilestone.build_collection(milestones)
+    end
+
+    it 'should have all project milestones' do
+      expect(@global_milestones.count).to eq(2)
+    end
+
+    it 'should have all project milestones titles' do
+      expect(@global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'VD-123'])
+    end
+
+    it 'should have all project milestones' do
+      expect(@global_milestones.map { |group_milestone| group_milestone.milestones.count }.sum).to eq(6)
+    end
+  end
+
+  describe :initialize do
+    before do
+      milestones =
+        [
+          milestone1_project1,
+          milestone1_project2,
+          milestone1_project3,
+        ]
+
+      @global_milestone = GlobalMilestone.new(milestone1_project1.title, milestones)
+    end
+
+    it 'should have exactly one group milestone' do
+      expect(@global_milestone.title).to eq('Milestone v1.2')
+    end
+
+    it 'should have all project milestones with the same title' do
+      expect(@global_milestone.milestones.count).to eq(3)
+    end
+  end
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 80638fc8db2216fff6c064ea96f83dad5186ca64..bbfc5535eec532794f2be9e720c8f0fe420663e7 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -11,6 +11,7 @@
 #  type        :string(255)
 #  description :string(255)      default(""), not null
 #  avatar      :string(255)
+#  public      :boolean          default(FALSE)
 #
 
 require 'spec_helper'
@@ -84,4 +85,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 623332cd2f95c16784f6958ad5443194cf4c78bf..c9aa1b063c660cd04f8bd9ab6b0984c1df4052fb 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -68,6 +68,43 @@ 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) }
 
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 6518213d71c2581449cb5706bb01cf7073a66e2a..511ee8cbd96521de19c941f32786003706e59964 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -8,6 +8,7 @@
 #  project_id :integer
 #  created_at :datetime
 #  updated_at :datetime
+#  template   :boolean          default(FALSE)
 #
 
 require 'spec_helper'
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 6aaf1c036b05aa231ed9a7fdd9a64865b1f80d9a..90af75ff0e3ff84dce44a33e7d00c4d6898edfef 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -20,6 +20,7 @@
 #  position          :integer          default(0)
 #  locked_at         :datetime
 #  updated_by_id     :integer
+#  merge_error       :string(255)
 #
 
 require 'spec_helper'
@@ -79,6 +80,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/namespace_spec.rb b/spec/models/namespace_spec.rb
index 1d72a9503ae553b83c448c89c27edb41357ef525..a98b9cb7321205b8791f4fbf47ed298ee4635ffe 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -11,6 +11,7 @@
 #  type        :string(255)
 #  description :string(255)      default(""), not null
 #  avatar      :string(255)
+#  public      :boolean          default(FALSE)
 #
 
 require 'spec_helper'
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_spec.rb b/spec/models/project_spec.rb
index f93935ebe3b057e8e69be5c6b8bf12f4382f34a3..8d7e6e76766645934e6830f9bc8a9a690bc63e09 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -415,12 +415,15 @@ describe Project do
     it { expect(project.ci_commit(commit.sha)).to eq(commit) }
   end
 
-  describe :enable_ci do
+  describe :builds_enabled do
     let(:project) { create :project }
 
-    before { project.enable_ci }
+    before { project.builds_enabled = true }
 
-    it { expect(project.gitlab_ci?).to be_truthy }
+    subject { project.builds_enabled }
+
+    it { is_expected.to eq(project.gitlab_ci_service.active) }
+    it { expect(project.builds_enabled?).to be_truthy }
     it { expect(project.gitlab_ci_project).to be_a(Ci::Project) }
   end
 
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 94802dcfb79f7486c7ca36ce4c618bac9aca611e..3b8891444476f7a48c7b06337eacaa426da29b41 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -184,6 +184,12 @@ describe ProjectWiki do
       subject.create_page("test page", "some content", :markdown, "commit message")
       expect(subject.pages.first.page.version.message).to eq("commit message")
     end
+
+    it 'updates project activity' do
+      expect(subject).to receive(:update_project_activity)
+
+      subject.create_page('Test Page', 'This is content')
+    end
   end
 
   describe "#update_page" do
@@ -205,6 +211,12 @@ describe ProjectWiki do
     it "sets the correct commit message" do
       expect(@page.version.message).to eq("updated page")
     end
+
+    it 'updates project activity' do
+      expect(subject).to receive(:update_project_activity)
+
+      subject.update_page(@gollum_page, 'Yet more content', :markdown, 'Updated page again')
+    end
   end
 
   describe "#delete_page" do
@@ -217,13 +229,19 @@ describe ProjectWiki do
       subject.delete_page(@page)
       expect(subject.pages.count).to eq(0)
     end
+
+    it 'updates project activity' do
+      expect(subject).to receive(:update_project_activity)
+
+      subject.delete_page(@page)
+    end
   end
 
   private
 
   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..72ecb442a361a7576704efff942c01c2d4250542
--- /dev/null
+++ b/spec/models/release_spec.rb
@@ -0,0 +1,28 @@
+# == Schema Information
+#
+# Table name: releases
+#
+#  id          :integer          not null, primary key
+#  tag         :string(255)
+#  description :text
+#  project_id  :integer
+#  created_at  :datetime
+#  updated_at  :datetime
+#
+
+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 05e51532eb878564afd1ad0db0f73cd2e634dcb3..319fa0a7c8d70cae60ade1943744b1b8c6581ea9 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/user_spec.rb b/spec/models/user_spec.rb
index c71cfb3ebe341bde2f5364835130b74b34e5326c..7d716c231206139e11641cb15c313c841b7e298b 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -54,6 +54,8 @@
 #  public_email               :string(255)      default(""), not null
 #  dashboard                  :integer          default(0)
 #  project_view               :integer          default(0)
+#  consumed_timestep          :integer
+#  layout                     :integer          default(0)
 #
 
 require 'spec_helper'
@@ -663,24 +665,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/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 580bbec77d18b93faed215a926a2b92c19723c8e..9fc294118ae12e35fee731274c5c4dbcf16cbefa 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -88,8 +88,11 @@ describe API::API, api: true  do
         end
 
         it 'returns projects in the correct order when ci_enabled_first parameter is passed' do
-          [project, project2, project3].each{ |project| project.build_missing_services }
-          project2.gitlab_ci_service.update(active: true)
+          [project, project2, project3].each do |project|
+            project.builds_enabled = false
+            project.build_missing_services
+          end
+          project2.builds_enabled = true
           get api('/projects', user), { ci_enabled_first: 'true' }
           expect(response.status).to eq(200)
           expect(json_response).to be_an Array
@@ -606,28 +609,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 1149f7e79895d80c58cf594302d39b5e6a5bd844..4911cdd9da666392af1b2fa2f29b32d27dc2f1c1 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -11,81 +11,6 @@ describe API::API, api: true  do
   let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) }
   let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) }
 
-  describe "GET /projects/:id/repository/tags" do
-    it "should return an array of project tags" do
-      get api("/projects/#{project.id}/repository/tags", user)
-      expect(response.status).to eq(200)
-      expect(json_response).to be_an Array
-      expect(json_response.first['name']).to eq(project.repo.tags.sort_by(&:name).reverse.first.name)
-    end
-  end
-
-  describe 'POST /projects/:id/repository/tags' do
-    context 'lightweight tags' do
-      it 'should create a new tag' do
-        post api("/projects/#{project.id}/repository/tags", user),
-             tag_name: 'v7.0.1',
-             ref: 'master'
-
-        expect(response.status).to eq(201)
-        expect(json_response['name']).to eq('v7.0.1')
-      end
-    end
-
-    context 'annotated tag' 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}))
-
-        post api("/projects/#{project.id}/repository/tags", user),
-             tag_name: 'v7.1.0',
-             ref: 'master',
-             message: 'Release 7.1.0'
-
-        expect(response.status).to eq(201)
-        expect(json_response['name']).to eq('v7.1.0')
-        expect(json_response['message']).to eq('Release 7.1.0')
-      end
-    end
-
-    it 'should deny for user without push access' do
-      post api("/projects/#{project.id}/repository/tags", user2),
-           tag_name: 'v1.9.0',
-           ref: '621491c677087aa243f165eab467bfdfbee00be1'
-      expect(response.status).to eq(403)
-    end
-
-    it 'should return 400 if tag name is invalid' do
-      post api("/projects/#{project.id}/repository/tags", user),
-           tag_name: 'v 1.0.0',
-           ref: 'master'
-      expect(response.status).to eq(400)
-      expect(json_response['message']).to eq('Tag name invalid')
-    end
-
-    it 'should return 400 if tag already exists' do
-      post api("/projects/#{project.id}/repository/tags", user),
-           tag_name: 'v8.0.0',
-           ref: 'master'
-      expect(response.status).to eq(201)
-      post api("/projects/#{project.id}/repository/tags", user),
-           tag_name: 'v8.0.0',
-           ref: 'master'
-      expect(response.status).to eq(400)
-      expect(json_response['message']).to eq('Tag already exists')
-    end
-
-    it 'should return 400 if ref name is invalid' do
-      post api("/projects/#{project.id}/repository/tags", user),
-           tag_name: 'mytag',
-           ref: 'foo'
-      expect(response.status).to eq(400)
-      expect(json_response['message']).to eq('Invalid reference name')
-    end
-  end
-
   describe "GET /projects/:id/repository/tree" do
     context "authorized user" do
       before { project.team << [user2, :reporter] }
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index c0226605a2320aebc5f85bea2daaeb5e6ba8ba86..b180d2fec77b8fd52197589b5e392c90bd1bf6ba 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -46,6 +46,7 @@ describe API::API, api: true  do
         delete api("/projects/#{project.id}/services/#{dashed_service}", user)
 
         expect(response.status).to eq(200)
+        project.send(service_method).reload
         expect(project.send(service_method).activated?).to be_falsey
       end
     end
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cc9a5f4758245b7fbc1f9c57d912c6104a86a826
--- /dev/null
+++ b/spec/requests/api/tags_spec.rb
@@ -0,0 +1,135 @@
+require 'spec_helper'
+require 'mime/types'
+
+describe API::API, api: true  do
+  include ApiHelpers
+  include RepoHelpers
+
+  let(:user) { create(:user) }
+  let(:user2) { create(:user) }
+  let!(:project) { create(:project, creator_id: user.id) }
+  let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) }
+  let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) }
+
+  describe "GET /projects/:id/repository/tags" do
+    let(:tag_name) { project.repository.tag_names.sort.reverse.first }
+    let(:description) { 'Awesome release!' }
+
+    context 'without releases' do
+      it "should return an array of project tags" do
+        get api("/projects/#{project.id}/repository/tags", user)
+        expect(response.status).to eq(200)
+        expect(json_response).to be_an Array
+        expect(json_response.first['name']).to eq(tag_name)
+      end
+    end
+
+    context 'with releases' do
+      before do
+        release = project.releases.find_or_initialize_by(tag: tag_name)
+        release.update_attributes(description: description)
+        get api("/projects/#{project.id}/repository/tags", user)
+      end
+
+      it "should return an array of project tags with release info" do
+        expect(response.status).to eq(200)
+        expect(json_response).to be_an Array
+        expect(json_response.first['name']).to eq(tag_name)
+        expect(json_response.first['release']['description']).to eq(description)
+      end
+    end
+  end
+
+  describe 'POST /projects/:id/repository/tags' do
+    context 'lightweight tags' do
+      it 'should create a new tag' do
+        post api("/projects/#{project.id}/repository/tags", user),
+             tag_name: 'v7.0.1',
+             ref: 'master'
+
+        expect(response.status).to eq(201)
+        expect(json_response['name']).to eq('v7.0.1')
+      end
+    end
+
+    context 'lightweight tags with release notes' do
+      it 'should create a new tag' do
+        post api("/projects/#{project.id}/repository/tags", user),
+             tag_name: 'v7.0.1',
+             ref: 'master',
+             release_description: 'Wow'
+
+        expect(response.status).to eq(201)
+        expect(json_response['name']).to eq('v7.0.1')
+        expect(json_response['release']['description']).to eq('Wow')
+      end
+    end
+
+    context 'annotated tag' 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(#{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',
+             ref: 'master',
+             message: 'Release 7.1.0'
+
+        expect(response.status).to eq(201)
+        expect(json_response['name']).to eq('v7.1.0')
+        expect(json_response['message']).to eq('Release 7.1.0')
+      end
+    end
+
+    it 'should deny for user without push access' do
+      post api("/projects/#{project.id}/repository/tags", user2),
+           tag_name: 'v1.9.0',
+           ref: '621491c677087aa243f165eab467bfdfbee00be1'
+      expect(response.status).to eq(403)
+    end
+
+    it 'should return 400 if tag name is invalid' do
+      post api("/projects/#{project.id}/repository/tags", user),
+           tag_name: 'v 1.0.0',
+           ref: 'master'
+      expect(response.status).to eq(400)
+      expect(json_response['message']).to eq('Tag name invalid')
+    end
+
+    it 'should return 400 if tag already exists' do
+      post api("/projects/#{project.id}/repository/tags", user),
+           tag_name: 'v8.0.0',
+           ref: 'master'
+      expect(response.status).to eq(201)
+      post api("/projects/#{project.id}/repository/tags", user),
+           tag_name: 'v8.0.0',
+           ref: 'master'
+      expect(response.status).to eq(400)
+      expect(json_response['message']).to eq('Tag already exists')
+    end
+
+    it 'should return 400 if ref name is invalid' do
+      post api("/projects/#{project.id}/repository/tags", user),
+           tag_name: 'mytag',
+           ref: 'foo'
+      expect(response.status).to eq(400)
+      expect(json_response['message']).to eq('Invalid reference name')
+    end
+  end
+
+  describe 'PUT /projects/:id/repository/:tag/release' do
+    let(:tag_name) { project.repository.tag_names.first }
+    let(:description) { 'Awesome release!' }
+
+    it 'should create description for existing git tag' do
+      put api("/projects/#{project.id}/repository/#{tag_name}/release", user),
+        description: description
+
+      expect(response.status).to eq(200)
+      expect(json_response['tag']).to eq(tag_name)
+      expect(json_response['description']).to eq(description)
+    end
+  end
+end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index d26a300ed8290c5089fdc6da549ec362ea2e41c3..a9ef2fe58858000a39fff1f1e3eb89899b2845ac 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -343,8 +343,9 @@ describe API::API, api: true  do
       end.to change{ user.keys.count }.by(1)
     end
 
-    it "should raise error for invalid ID" do
-      expect{post api("/users/ASDF/keys", admin) }.to raise_error(ActionController::RoutingError)
+    it "should return 405 for invalid ID" do
+      post api("/users/ASDF/keys", admin)
+      expect(response.status).to eq(405)
     end
   end
 
@@ -374,9 +375,9 @@ describe API::API, api: true  do
         expect(json_response.first['title']).to eq(key.title)
       end
 
-      it "should return 404 for invalid ID" do
+      it "should return 405 for invalid ID" do
         get api("/users/ASDF/keys", admin)
-        expect(response.status).to eq(404)
+        expect(response.status).to eq(405)
       end
     end
   end
@@ -434,7 +435,8 @@ describe API::API, api: true  do
     end
 
     it "should raise error for invalid ID" do
-      expect{post api("/users/ASDF/emails", admin) }.to raise_error(ActionController::RoutingError)
+      post api("/users/ASDF/emails", admin)
+      expect(response.status).to eq(405)
     end
   end
 
@@ -465,7 +467,8 @@ describe API::API, api: true  do
       end
 
       it "should raise error for invalid ID" do
-        expect{put api("/users/ASDF/emails", admin) }.to raise_error(ActionController::RoutingError)
+        put api("/users/ASDF/emails", admin)
+        expect(response.status).to eq(405)
       end
     end
   end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 88218a93e1f589b90e015b20bde21c28fe9d59c4..c2be045099dc2614bb1f8afb4d7166816a082c12 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -5,7 +5,7 @@ describe Ci::API::API do
 
   let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) }
   let(:project) { FactoryGirl.create(:ci_project) }
-  let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+  let(:gl_project) { project.gl_project }
 
   before do
     stub_ci_commit_to_return_yaml_file
@@ -14,7 +14,7 @@ describe Ci::API::API do
   describe "Builds API for runners" do
     let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") }
     let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") }
-    let(:shared_gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: shared_project) }
+    let(:shared_gl_project) { shared_project.gl_project }
 
     before do
       FactoryGirl.create :ci_runner_project, project_id: project.id, runner_id: runner.id
@@ -41,7 +41,7 @@ describe Ci::API::API do
 
       it "should return 404 error if no builds for specific runner" do
         commit = FactoryGirl.create(:ci_commit, gl_project: shared_gl_project)
-        FactoryGirl.create(:ci_build, commit: commit, status: 'pending' )
+        FactoryGirl.create(:ci_build, commit: commit, status: 'pending')
 
         post ci_api("/builds/register"), token: runner.token
 
@@ -50,7 +50,7 @@ describe Ci::API::API do
 
       it "should return 404 error if no builds for shared runner" do
         commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
-        FactoryGirl.create(:ci_build, commit: commit, status: 'pending' )
+        FactoryGirl.create(:ci_build, commit: commit, status: 'pending')
 
         post ci_api("/builds/register"), token: shared_runner.token
 
@@ -79,7 +79,7 @@ describe Ci::API::API do
           { "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 },
+          { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }
         ])
       end
 
@@ -122,5 +122,191 @@ describe Ci::API::API do
         expect(build.reload.trace).to eq 'hello_world'
       end
     end
+
+    context "Artifacts" do
+      let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+      let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') }
+      let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
+      let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) }
+      let(:authorize_url) { ci_api("/builds/#{build.id}/artifacts/authorize") }
+      let(:post_url) { ci_api("/builds/#{build.id}/artifacts") }
+      let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") }
+      let(:get_url) { ci_api("/builds/#{build.id}/artifacts") }
+      let(:headers) { { "GitLab-Workhorse" => "1.0" } }
+      let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.project.token) }
+
+      describe "POST /builds/:id/artifacts/authorize" do
+        context "should authorize posting artifact to running build" do
+          before do
+            build.run!
+          end
+
+          it "using token as parameter" do
+            post authorize_url, { token: build.project.token }, headers
+            expect(response.status).to eq(200)
+            expect(json_response["TempPath"]).to_not be_nil
+          end
+
+          it "using token as header" do
+            post authorize_url, {}, headers_with_token
+            expect(response.status).to eq(200)
+            expect(json_response["TempPath"]).to_not be_nil
+          end
+        end
+
+        context "should fail to post too large artifact" do
+          before do
+            build.run!
+          end
+
+          it "using token as parameter" do
+            stub_application_setting(max_artifacts_size: 0)
+            post authorize_url, { token: build.project.token, filesize: 100 }, headers
+            expect(response.status).to eq(413)
+          end
+
+          it "using token as header" do
+            stub_application_setting(max_artifacts_size: 0)
+            post authorize_url, { filesize: 100 }, headers_with_token
+            expect(response.status).to eq(413)
+          end
+        end
+
+        context "should get denied" do
+          it do
+            post authorize_url, { token: 'invalid', filesize: 100 }
+            expect(response.status).to eq(403)
+          end
+        end
+      end
+
+      describe "POST /builds/:id/artifacts" do
+        context "Disable sanitizer" do
+          before do
+            # by configuring this path we allow to pass temp file from any path
+            allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return('/')
+          end
+
+          context "should post artifact to running build" do
+            before do
+              build.run!
+            end
+
+            it "uses regual file post" do
+              upload_artifacts(file_upload, headers_with_token, false)
+              expect(response.status).to eq(201)
+              expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename)
+            end
+
+            it "uses accelerated file post" do
+              upload_artifacts(file_upload, headers_with_token, true)
+              expect(response.status).to eq(201)
+              expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename)
+            end
+
+            it "updates artifact" do
+              upload_artifacts(file_upload, headers_with_token)
+              upload_artifacts(file_upload2, headers_with_token)
+              expect(response.status).to eq(201)
+              expect(json_response["artifacts_file"]["filename"]).to eq(file_upload2.original_filename)
+            end
+          end
+
+          context "should fail to post too large artifact" do
+            before do
+              build.run!
+            end
+
+            it do
+              stub_application_setting(max_artifacts_size: 0)
+              upload_artifacts(file_upload, headers_with_token)
+              expect(response.status).to eq(413)
+            end
+          end
+
+          context "should fail to post artifacts without file" do
+            before do
+              build.run!
+            end
+
+            it do
+              post post_url, {}, headers_with_token
+              expect(response.status).to eq(400)
+            end
+          end
+
+          context "should fail to post artifacts without GitLab-Workhorse" do
+            before do
+              build.run!
+            end
+
+            it do
+              post post_url, { token: build.project.token }, {}
+              expect(response.status).to eq(403)
+            end
+          end
+        end
+
+        context "should fail to post artifacts for outside of tmp path" do
+          before do
+            # by configuring this path we allow to pass file from @tmpdir only
+            # but all temporary files are stored in system tmp directory
+            @tmpdir = Dir.mktmpdir
+            allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return(@tmpdir)
+            build.run!
+          end
+
+          after do
+            FileUtils.remove_entry @tmpdir
+          end
+
+          it do
+            upload_artifacts(file_upload, headers_with_token)
+            expect(response.status).to eq(400)
+          end
+        end
+
+        def upload_artifacts(file, headers = {}, accelerated = true)
+          if accelerated
+            post post_url, {
+              'file.path' => file.path,
+              'file.name' => file.original_filename
+            }, headers
+          else
+            post post_url, { file: file }, headers
+          end
+        end
+      end
+
+      describe "DELETE /builds/:id/artifacts" do
+        before do
+          build.run!
+          post delete_url, token: build.project.token, file: file_upload
+        end
+
+        it "should delete artifact build" do
+          build.success
+          delete delete_url, token: build.project.token
+          expect(response.status).to eq(200)
+        end
+      end
+
+      describe "GET /builds/:id/artifacts" do
+        before do
+          build.run!
+        end
+
+        it "should download artifact" do
+          build.update_attributes(artifacts_file: file_upload)
+          get get_url, token: build.project.token
+          expect(response.status).to eq(200)
+        end
+
+        it "should fail to download if no artifact uploaded" do
+          get get_url, token: build.project.token
+          expect(response.status).to eq(404)
+        end
+      end
+    end
   end
 end
diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb
index 6049135fd104f799c8757e81824dfc185a7c5495..aa51ba95bcadc1a545bbe9c9a12d5f271a64af1c 100644
--- a/spec/requests/ci/api/commits_spec.rb
+++ b/spec/requests/ci/api/commits_spec.rb
@@ -4,7 +4,7 @@ describe Ci::API::API, 'Commits' do
   include ApiHelpers
 
   let(:project) { FactoryGirl.create(:ci_project) }
-  let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+  let(:gl_project) { project.gl_project }
   let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
 
   let(:options) do
diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb
index 53f7f91cc1f7c0a89583ceb294ae794f721c03ff..893fd168d3e9c9cf90b40d633fdd714c3221a6be 100644
--- a/spec/requests/ci/api/projects_spec.rb
+++ b/spec/requests/ci/api/projects_spec.rb
@@ -41,8 +41,8 @@ describe Ci::API::API do
     describe "GET /projects/owned" do
       let!(:gl_project1) {FactoryGirl.create(:empty_project, namespace: user.namespace)}
       let!(:gl_project2) {FactoryGirl.create(:empty_project, namespace: user.namespace)}
-      let!(:project1) { FactoryGirl.create(:ci_project, gl_project: gl_project1) }
-      let!(:project2) { FactoryGirl.create(:ci_project, gl_project: gl_project2) }
+      let!(:project1) { gl_project1.ensure_gitlab_ci_project }
+      let!(:project2) { gl_project2.ensure_gitlab_ci_project }
 
       before do
         project1.gl_project.team << [user, :developer]
@@ -180,87 +180,53 @@ describe Ci::API::API do
     end
   end
 
-  describe "POST /projects" do
-    let(:gl_project) { FactoryGirl.create :empty_project }
-    let(:project_info) do
-      {
-        gitlab_id: gl_project.id
-      }
-    end
-
-    let(:invalid_project_info) { {} }
+  describe "POST /projects/:id/runners/:id" do
+    let(:project) { FactoryGirl.create(:ci_project) }
+    let(:runner) { FactoryGirl.create(:ci_runner) }
 
-    context "with valid project info" do
-      before do
-        options.merge!(project_info)
-      end
+    it "should add the project to the runner" do
+      project.gl_project.team << [user, :master]
+      post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+      expect(response.status).to eq(201)
 
-      it "should create a project with valid data" do
-        post ci_api("/projects"), options
-        expect(response.status).to eq(201)
-        expect(json_response['name']).to eq(gl_project.name_with_namespace)
-      end
+      project.reload
+      expect(project.runners.first.id).to eq(runner.id)
     end
 
-    context "with invalid project info" do
-      before do
-        options.merge!(invalid_project_info)
-      end
+    it "should fail if it tries to link a non-existing project or runner" do
+      post ci_api("/projects/#{project.id}/runners/non-existing"), options
+      expect(response.status).to eq(404)
 
-      it "should error with invalid data" do
-        post ci_api("/projects"), options
-        expect(response.status).to eq(400)
-      end
+      post ci_api("/projects/non-existing/runners/#{runner.id}"), options
+      expect(response.status).to eq(404)
     end
 
-    describe "POST /projects/:id/runners/:id" do
-      let(:project) { FactoryGirl.create(:ci_project) }
-      let(:runner) { FactoryGirl.create(:ci_runner) }
-
-      it "should add the project to the runner" do
-        project.gl_project.team << [user, :master]
-        post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
-        expect(response.status).to eq(201)
-
-        project.reload
-        expect(project.runners.first.id).to eq(runner.id)
-      end
-
-      it "should fail if it tries to link a non-existing project or runner" do
-        post ci_api("/projects/#{project.id}/runners/non-existing"), options
-        expect(response.status).to eq(404)
-
-        post ci_api("/projects/non-existing/runners/#{runner.id}"), options
-        expect(response.status).to eq(404)
-      end
-
-      it "non-manager is not authorized" do
-        allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false)
-        post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
-        expect(response.status).to eq(401)
-      end
+    it "non-manager is not authorized" do
+      allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false)
+      post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+      expect(response.status).to eq(401)
     end
+  end
 
-    describe "DELETE /projects/:id/runners/:id" do
-      let(:project) { FactoryGirl.create(:ci_project) }
-      let(:runner) { FactoryGirl.create(:ci_runner) }
+  describe "DELETE /projects/:id/runners/:id" do
+    let(:project) { FactoryGirl.create(:ci_project) }
+    let(:runner) { FactoryGirl.create(:ci_runner) }
 
-      it "should remove the project from the runner" do
-        project.gl_project.team << [user, :master]
-        post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+    it "should remove the project from the runner" do
+      project.gl_project.team << [user, :master]
+      post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
 
-        expect(project.runners).to be_present
-        delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
-        expect(response.status).to eq(200)
+      expect(project.runners).to be_present
+      delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+      expect(response.status).to eq(200)
 
-        project.reload
-        expect(project.runners).to be_empty
-      end
+      project.reload
+      expect(project.runners).to be_empty
+    end
 
-      it "non-manager is not authorized" do
-        delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
-        expect(response.status).to eq(401)
-      end
+    it "non-manager is not authorized" do
+      delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+      expect(response.status).to eq(401)
     end
   end
 end
diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb
index 93617fc4b3f2dc9eee0b9656a3d53f1aab77b7e3..a2b436d58119afc69f80b8affc9bc0f1c4c85a9e 100644
--- a/spec/requests/ci/api/triggers_spec.rb
+++ b/spec/requests/ci/api/triggers_spec.rb
@@ -6,7 +6,7 @@ describe Ci::API::API do
   describe 'POST /projects/:project_id/refs/:ref/trigger' do
     let!(:trigger_token) { 'secure token' }
     let!(:gl_project) { FactoryGirl.create(:project) }
-    let!(:project) { FactoryGirl.create(:ci_project, gl_project: gl_project) }
+    let!(:project) { gl_project.ensure_gitlab_ci_project }
     let!(:project2) { FactoryGirl.create(:ci_project) }
     let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) }
     let(:options) do
diff --git a/spec/services/archive_repository_service_spec.rb b/spec/services/archive_repository_service_spec.rb
index 1cc7b2402168112d5c9079dc92fcc164ecc7abe9..f7a36cd96705025a15ba2ce40b15a19f37cdc3fb 100644
--- a/spec/services/archive_repository_service_spec.rb
+++ b/spec/services/archive_repository_service_spec.rb
@@ -6,7 +6,7 @@ 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
diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb
index fcafae386448090f89f881b6b5dd5d7508847855..2ef4bb50a57d0edb5745b4d37939961fa050c27c 100644
--- a/spec/services/ci/create_trigger_request_service_spec.rb
+++ b/spec/services/ci/create_trigger_request_service_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe Ci::CreateTriggerRequestService do
   let(:service) { Ci::CreateTriggerRequestService.new }
   let(:gl_project) { create(:project) }
-  let(:project) { create(:ci_project, gl_project: gl_project) }
+  let(:project) { gl_project.ensure_gitlab_ci_project }
   let(:trigger) { create(:ci_trigger, project: project) }
 
   before do
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 fd72905c331ab3f72ed61d66b3f3834bb6cec0ae..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
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/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..034c0f22e12d9c7d7b008587eecfdf36d7972079
--- /dev/null
+++ b/spec/services/milestones/close_service_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Milestones::CloseService do
+  let(:user) { create(:user) }
+  let(:project) { create(:project) }
+  let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) }
+
+  before do
+    project.team << [user, :master]
+  end
+
+  describe :execute do
+    before do
+      Milestones::CloseService.new(project, user, {}).execute(milestone)
+    end
+
+    it { expect(milestone).to be_valid }
+    it { expect(milestone).to be_closed }
+
+    describe :event do
+      let(:event) { Event.first }
+
+      it { expect(event.milestone).to be_truthy }
+      it { expect(event.target).to eq(milestone) }
+      it { expect(event.action_name).to eq('closed') }
+    end
+  end
+end
diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..757c9a226d80d1c9057c928427c3f6d385ee6504
--- /dev/null
+++ b/spec/services/milestones/create_service_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Milestones::CreateService do
+  let(:project) { create(:empty_project) }
+  let(:user) { create(:user) }
+
+  describe :execute do
+    context "valid params" do
+      before do
+        project.team << [user, :master]
+
+        opts = {
+          title: 'v2.1.9',
+          description: 'Patch release to fix security issue'
+        }
+
+        @milestone = Milestones::CreateService.new(project, user, opts).execute
+      end
+
+      it { expect(@milestone).to be_valid }
+      it { expect(@milestone.title).to eq('v2.1.9') }
+    end
+  end
+end
diff --git a/spec/services/milestones/group_service_spec.rb b/spec/services/milestones/group_service_spec.rb
deleted file mode 100644
index 74eb0f99e0f29e713c49fd27945bf573fbf13816..0000000000000000000000000000000000000000
--- a/spec/services/milestones/group_service_spec.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-require 'spec_helper'
-
-describe Milestones::GroupService do
-  let(:user) { create(:user) }
-  let(:user2) { create(:user) }
-  let(:group) { create(:group) }
-  let(:project1) { create(:project, group: group) }
-  let(:project2) { create(:project, path: 'gitlab-ci', group: group) }
-  let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) }
-  let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) }
-  let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) }
-  let(:milestone1_project3) { create(:milestone, title: "Milestone v1.2", project: project3) }
-  let(:milestone2_project1) { create(:milestone, title: "VD-123", project: project1) }
-  let(:milestone2_project2) { create(:milestone, title: "VD-123", project: project2) }
-  let(:milestone2_project3) { create(:milestone, title: "VD-123", project: project3) }
-
-  describe 'execute' do
-    context 'with valid projects' do
-      before do
-        milestones =
-          [ 
-            milestone1_project1,
-            milestone1_project2,
-            milestone1_project3,
-            milestone2_project1,
-            milestone2_project2,
-            milestone2_project3
-          ]
-        @group_milestones = Milestones::GroupService.new(milestones).execute
-      end
-
-      it 'should have all project milestones' do
-        expect(@group_milestones.count).to eq(2)
-      end
-
-      it 'should have all project milestones titles' do
-        expect(@group_milestones.map { |group_milestone| group_milestone.title }).to match_array(['Milestone v1.2', 'VD-123'])
-      end
-
-      it 'should have all project milestones' do
-        expect(@group_milestones.map { |group_milestone| group_milestone.milestones.count }.sum).to eq(6)
-      end
-    end
-  end
-
-  describe 'milestone' do
-    context 'with valid title' do
-      before do
-        milestones =
-          [ 
-            milestone1_project1,
-            milestone1_project2,
-            milestone1_project3,
-            milestone2_project1,
-            milestone2_project2,
-            milestone2_project3
-          ]
-        @group_milestones = Milestones::GroupService.new(milestones).milestone('Milestone v1.2')
-      end
-
-      it 'should have exactly one group milestone' do
-        expect(@group_milestones.title).to eq('Milestone v1.2')
-      end
-
-      it 'should have all project milestones with the same title' do
-        expect(@group_milestones.milestones.count).to eq(3)
-      end
-    end
-  end
-end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 25277f07482485dc312650ce14567cc447f3bc51..e81c4edb7d83ea7f08218cea5964b9053f34d39a 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -70,6 +70,28 @@ describe Projects::CreateService do
       end
     end
 
+    context 'builds_enabled global setting' do
+      let(:project) { create_project(@user, @opts) }
+
+      subject { project.builds_enabled? }
+
+      context 'global builds_enabled false does not enable CI by default' do
+        before do
+          @opts.merge!(builds_enabled: false)
+        end
+
+        it { is_expected.to be_falsey }
+      end
+
+      context 'global builds_enabled true does enable CI by default' do
+        before do
+          @opts.merge!(builds_enabled: true)
+        end
+
+        it { is_expected.to be_truthy }
+      end
+    end
+
     context 'restricted visibility level' do
       before do
         stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 65a8c81204d3c3a509b4c302934c05cd9fb9b539..e397b2b9b4a0133d0f419df317931911f55f2aa8 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -46,7 +46,7 @@ describe Projects::ForkService do
       it "fork and enable CI for fork" do
         @from_project.enable_ci
         @to_project = fork_project(@from_project, @to_user)
-        expect(@to_project.gitlab_ci?).to be_truthy
+        expect(@to_project.builds_enabled?).to be_truthy
       end
     end
   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 f584904845e63c1034274328dff93a6f0814d6af..3bb568f4d494858c432f259cf37c20ef7465f535 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -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)
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index d12ba25b71b85ea9c51dcbe4d9f8c19522c65ce6..787670e929792e5080975af31f4b5dc50dd96cf0 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -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..06559c3925d5b8e08e9c0da253210db2b8d92355 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -16,7 +16,7 @@ describe 'gitlab:app namespace rake task' do
   end
 
   def reenable_backup_sub_tasks
-    %w{db repo uploads builds}.each do |subtask|
+    %w{db repo uploads builds artifacts}.each do |subtask|
       Rake::Task["gitlab:backup:#{subtask}:create"].reenable
     end
   end
@@ -55,6 +55,8 @@ 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:backup:artifacts: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,19 +114,20 @@ 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 artifacts.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).to match('artifacts.tar.gz')
+      expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|artifacts.tar.gz)\/$/)
     end
 
     it 'should delete temp directories' do
       temp_dirs = Dir.glob(
-        File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds}')
+        File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts}')
       )
 
       expect(temp_dirs).to be_empty
@@ -160,12 +163,13 @@ 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 artifacts.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).to match('artifacts.tar.gz')
       expect(tar_contents).not_to match('repositories/')
     end
 
@@ -177,6 +181,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"]).not_to receive :invoke
       expect(Rake::Task["gitlab:backup:builds:restore"]).to receive :invoke
+      expect(Rake::Task["gitlab:backup:artifacts: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
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