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 cf44a5c7c824b711c8affe18140931b79bed48c2..3c22df7c9a361d9382d0865d9d6ea3e20f5ef77a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,7 +1,13 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
 v 8.2.0 (unreleased)
-  - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
+  - 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
@@ -12,11 +18,47 @@ v 8.2.0 (unreleased)
   - 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
-  - Use issue editor as cross reference comment author when issue is edited with a new mention.
+  - [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.1
+v 8.1.2
   - Fix cloning Wiki repositories via HTTP (Stan Hu)
   - Add migration to remove satellites directory
   - Fix specific runners visibility
@@ -26,13 +68,15 @@ v 8.1.1
   - 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)
-
-v 8.1.0 (unreleased)
+  - 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.
@@ -70,6 +114,7 @@ v 8.1.0 (unreleased)
   - 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
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9f79ff413a07f0bb181e3b8e9eb4ea162f9ba760..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
@@ -181,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_GIT_HTTP_SERVER_VERSION b/GITLAB_GIT_HTTP_SERVER_VERSION
deleted file mode 100644
index 0d91a54c7d439e84e3dd17d3594f1b2b6737f430..0000000000000000000000000000000000000000
--- a/GITLAB_GIT_HTTP_SERVER_VERSION
+++ /dev/null
@@ -1 +0,0 @@
-0.3.0
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 bb31c147b26886f9d4601adbb749de8e4a70213d..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'
@@ -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'
@@ -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 65abc45ff195a9d64792e70814eb65be29653324..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)
@@ -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)
@@ -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,21 +827,23 @@ 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)
@@ -859,6 +868,7 @@ DEPENDENCIES
   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,11 +885,10 @@ 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.3)
   redis-rails (~> 4.0.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 d42168a7231307dbb5dacb45ab94632bf078e674..a4b0c83644b9a8c1d2a7d2313ea38c72ac5f1f77 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -119,6 +119,6 @@ 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][https://gitlab.com/gitlab-org/gitlab-ce/issues].
+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].
+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/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/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee
index 050888f9c158325982a9513b7b47f612256fc5c2..f6bf836f19f3e4e594c4c077581ac6439371bde4 100644
--- a/app/assets/javascripts/blob/edit_blob.js.coffee
+++ b/app/assets/javascripts/blob/edit_blob.js.coffee
@@ -11,10 +11,10 @@ class @EditBlob
     if ace_mode
       editor.getSession().setMode "ace/mode/" + ace_mode
 
-    $(".js-commit-button").click ->
-      $("#file-content").val editor.getValue()
-      $(".file-editor form").submit()
-      return false
+    # Before a form submission, move the content from the Ace editor into the
+    # submitted textarea
+    $('form').submit ->
+      $("#file-content").val(editor.getValue())
 
     editModePanes = $(".js-edit-mode-pane")
     editModeLinks = $(".js-edit-mode a")
diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee
index 1f36a53f1917ce74ce32cb6aa69ec9e6ebc4f1fa..68c5e5195e396b5ef3a38ce187be52535037133c 100644
--- a/app/assets/javascripts/blob/new_blob.js.coffee
+++ b/app/assets/javascripts/blob/new_blob.js.coffee
@@ -11,10 +11,10 @@ class @NewBlob
     if ace_mode
       editor.getSession().setMode "ace/mode/" + ace_mode
 
-    $(".js-commit-button").click ->
-      $("#file-content").val editor.getValue()
-      $(".file-editor form").submit()
-      return false
+    # Before a form submission, move the content from the Ace editor into the
+    # submitted textarea
+    $('form').submit ->
+      $("#file-content").val(editor.getValue())
 
   editor: ->
     return @editor
diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee
index 4c4bc3d66edcd196f56c436bd2e89fa77290e2e4..976212369245616a4adc793ad8991b861a871d01 100644
--- a/app/assets/javascripts/calendar.js.coffee
+++ b/app/assets/javascripts/calendar.js.coffee
@@ -25,7 +25,7 @@ class @Calendar
         30
       ]
       legendCellPadding: 3
-      cellSize: $('.user-calendar').width() / 80
+      cellSize: $('.user-calendar').width() / 73
       onClick: (date, count) ->
         formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
         $.ajax
diff --git a/app/assets/javascripts/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/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index ea75c656bccb5025a2b556913d6b66aefda73c32..b0682f16845f83071d30194aa6ac2c57fc494ad2 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -29,7 +29,6 @@ class @Notes
     $(document).on "ajax:success", "form.edit_note", @updateNote
 
     # Edit note link
-    $(document).on "click", ".js-note-edit", @showEditForm
     $(document).on "click", ".note-edit-cancel", @cancelEdit
 
     # Reopen and close actions for Issue/MR combined with note form submit
@@ -67,7 +66,6 @@ class @Notes
     $(document).off "ajax:success", ".js-main-target-form"
     $(document).off "ajax:success", ".js-discussion-note-form"
     $(document).off "ajax:success", "form.edit_note"
-    $(document).off "click", ".js-note-edit"
     $(document).off "click", ".note-edit-cancel"
     $(document).off "click", ".js-note-delete"
     $(document).off "click", ".js-note-attachment-delete"
@@ -287,13 +285,14 @@ class @Notes
   Adds a hidden div with the original content of the note to fill the edit note form with
   if the user cancels
   ###
-  showEditForm: (e) ->
-    e.preventDefault()
-    note = $(this).closest(".note")
+  showEditForm: (note, formHTML) ->
+    nodeText = note.find(".note-text");
+    nodeText.hide()
+    note.find('.note-edit-form').remove()
+    nodeText.after(formHTML)
     note.find(".note-body > .note-text").hide()
     note.find(".note-header").hide()
-    base_form = note.find(".note-edit-form")
-    form = base_form.clone().insertAfter(base_form)
+    form = note.find(".note-edit-form")
     form.addClass('current-note-edit-form gfm-form')
     form.find('.div-dropzone').remove()
 
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 5949a0fd5adc0ac65509f2c675170f9b5f4acccf..1635df9c97b3081be3dcb1d7d1d742604c26328d 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -28,6 +28,10 @@
   border-bottom: 1px solid $border-color;
   color: $gl-gray;
 
+  &.oneline-block {
+    line-height: 42px;
+  }
+
   &.white {
     background-color: white;
   }
@@ -100,7 +104,7 @@
   }
 
   .cover-desc {
-    padding: 0 $gl-padding;
+    padding: 0 $gl-padding 3px;
     color: $gl-text-color;
   }
 
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 04024419584e39ef6c732e6f92b5c667aeb00181..fe56266284b9d5c5709ce5d2cdd0f2d00eb6226e 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -180,3 +180,7 @@
     }
   }
 }
+
+.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/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 f6942db5816966e22989f3505a25a449d4497226..45f3b5849bfc47cfb05d60790054838c58c232e5 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -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 fe078d016d7a267ecb7f85b8cec3f5620dd57f8e..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;
       }
     }
 
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 985ea16457621c91278182d3cd33a5a1e1b9f06a..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,6 +240,7 @@
       width: 100%;
       padding: 10px 22px;
       overflow: hidden;
+      outline: none;
 
       img {
         width: 36px;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index e6558a2385802336acff5444bd4be4660ba822e3..ba0312ba0db5692549945a4f56dc7d928129e0ae 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -173,7 +173,6 @@
  *
  */
 body {
-  text-rendering:optimizeLegibility;
   -webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px;
 }
 
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/events.scss b/app/assets/stylesheets/pages/events.scss
index dfb901652bf8480938773975f907852178c602d2..282aaf2219b97b54f029d673533c13bc454e780f 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -4,7 +4,7 @@
  */
 .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 $table-border-color;
@@ -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/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index f0b3667accacf29df3f96e4bcdd98073348c7688..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 {
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/profile.scss b/app/assets/stylesheets/pages/profile.scss
index b7391e5303bff8f0bdbc5096c78fdd1e90af0125..1d6ca0dfc1302663afdd50099bf7cf78a974aa7b 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -53,3 +53,25 @@
   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 6eb659dae17acb7559e585fd6a9c95798b5e8b1d..d3b10040022e71ce86097277792e8d728dab8054 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -552,4 +552,4 @@ pre.light-well {
     z-index: 100;
     position: relative;
   }
-}
+}
\ No newline at end of file
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/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 039f18f23e0cf18bc432ea45ef26bb54c3eb7b3f..a9bcfc7456ad394b64ec2c10939b38de44a0c67e 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -57,6 +57,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
       :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/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 c63d0793e3141dde9c10d2fb500b374004d04d08..d7c927d444cbc4b43d0d5bfe20f63bb8e2c5a5c8 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -63,12 +63,6 @@ class Admin::UsersController < Admin::ApplicationController
     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),
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 1b0609e279ef19e1c2580620e2abcf94d468c31a..0d182e8eb04d92fded1152636459a9be917d82c1 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -59,13 +59,8 @@ class ApplicationController < ActionController::Base
   end
 
   def authenticate_user!(*args)
-    # If user is not signed-in and tries to access root_path - redirect him to landing page
-    # Don't redirect to the default URL to prevent endless redirections
-    if current_application_settings.home_page_url.present? &&
-        current_application_settings.home_page_url.chomp('/') != Gitlab.config.gitlab['url'].chomp('/')
-      if current_user.nil? && root_path == request.path
-        redirect_to current_application_settings.home_page_url and return
-      end
+    if redirect_to_home_page_url?
+      redirect_to current_application_settings.home_page_url and return
     end
 
     super(*args)
@@ -346,4 +341,17 @@ class ApplicationController < ActionController::Base
   def git_import_enabled?
     current_application_settings.import_sources.include?('git')
   end
+
+  def redirect_to_home_page_url?
+    # If user is not signed-in and tries to access root_path - redirect him to landing page
+    # Don't redirect to the default URL to prevent endless redirections
+    return false unless current_application_settings.home_page_url.present?
+
+    home_page_url = current_application_settings.home_page_url.chomp('/')
+    root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]
+
+    return false if root_urls.include?(home_page_url)
+
+    current_user.nil? && root_path == request.path
+  end
 end
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index 809b44387ba0df263dc335101cc8dd884367a176..8406399fb60a3cb98d7d1279b3f9feb19af31512 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -26,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/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/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/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 7d72e0b951b5108e684de7174e53dca41337cfdb..4638f77b887e7440ea0b63a3e7629ff62b919d67 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -3,6 +3,7 @@ class Projects::BuildsController < Projects::ApplicationController
   before_action :build, except: [:index, :cancel_all]
 
   before_action :authorize_manage_builds!, except: [:index, :show, :status]
+  before_action :authorize_download_build_artifacts!, only: [:download]
 
   layout "project"
 
@@ -30,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|
@@ -42,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
@@ -71,6 +80,10 @@ 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
@@ -80,4 +93,14 @@ class Projects::BuildsController < Projects::ApplicationController
       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/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 878c3a66e7df2820906d9fedc2d56875e5b3536c..deefdd766678ce6dc16999c3f4902c37755639bf 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -7,14 +7,14 @@ class Projects::CommitController < Projects::ApplicationController
   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
@@ -23,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 }
@@ -32,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)
@@ -53,11 +56,22 @@ class Projects::CommitController < Projects::ApplicationController
     render layout: false
   end
 
+  private
+
   def commit
     @commit ||= @project.commit(params[:id])
   end
 
-  private
+  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)
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 71aaad1fad61d565a67250afd6b55f1ad17bd900..55134e11d15907c673f04219f34049e694f472d4 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -12,9 +12,10 @@ 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 = Commit.decorate(compare_result.commits, @project)
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index e767efbdc0cfd168c51670ac5b1b6fcad6b7feac..e74c2905e4849dd3230c6729508cb5719435550b 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -158,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 16c42386623347e43ec941e93b1ca138b6c7fa0d..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
@@ -275,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/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 41cd08c93c60bfed5fa7ac6f0e14c85055f86162..0c98e2f1bfdbd119c5db38d43da2ddb70e70ca10 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController
   before_action :authorize_read_note!
   before_action :authorize_create_note!, only: [:create]
   before_action :authorize_admin_note!, only: [:update, :destroy]
-  before_action :find_current_user_notes, except: [:destroy, :delete_attachment]
+  before_action :find_current_user_notes, except: [:destroy, :edit, :delete_attachment]
 
   def index
     current_fetched_at = Time.now.to_i
@@ -29,6 +29,11 @@ class Projects::NotesController < Projects::ApplicationController
     end
   end
 
+  def edit
+    @note = note
+    render layout: false
+  end
+
   def update
     @note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
 
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/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 05c7d3de8bc25c9c02b612e2e02b9c7270fdd6b0..23453195e8521a3603beca537b2503432a6b8c19 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -1,7 +1,7 @@
 class ProjectsController < ApplicationController
   include ExtractsPath
 
-  prepend_before_filter :render_go_import, only: [:show]
+  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]
@@ -72,8 +72,7 @@ class ProjectsController < ApplicationController
   def remove_fork
     return access_denied! unless can?(current_user, :remove_fork_project, @project)
 
-    if @project.forked?
-      @project.forked_project_link.destroy
+    if @project.unlink_fork
       flash[:notice] = 'The fork relationship has been removed.'
     end
   end
@@ -213,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
 
@@ -242,7 +242,7 @@ class ProjectsController < ApplicationController
     project.repository_exists? && !project.empty_repo?
   end
 
-  # Override get_id from ExtractsPath, which returns the branch and file path 
+  # 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
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/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/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 ed88df5dd8691513191cb49aca41642f50f72135..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)
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index e65e37211c4151a80921cfe253d4c26db227eed8..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
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/issues_helper.rb b/app/helpers/issues_helper.rb
index fda18e7b316d7c130338ac6e85a47eaf0d17d3bc..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
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index ee04ace35d0933e74386739f275c625d4a6ecedb..795fb439f256ba5bba14acda80f938fbd61b91c7 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -100,7 +100,7 @@ module LabelsHelper
         Label.where(project_id: @projects)
       end
 
-    grouped_labels = Labels::GroupService.new(labels).execute
+    grouped_labels = GlobalLabel.build_collection(labels)
     grouped_labels.unshift(Label::None)
     grouped_labels.unshift(Label::Any)
 
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/projects_helper.rb b/app/helpers/projects_helper.rb
index 5301c2ccf7688a0b293f28fe27b3577e23cde479..690ae2090db69c55e4223d1664b1bfbafbcb4544 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -117,7 +117,7 @@ module ProjectsHelper
       nav_tabs << :merge_requests
     end
 
-    if project.gitlab_ci? && 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/models/ability.rb b/app/models/ability.rb
index 6526cc6bc6a4ec60ea87138904d9fd33e19b3509..eef481c8f8a594ca84e76ae80eb3bbf2b3af4587 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -155,6 +155,7 @@ class Ability
         :create_merge_request,
         :create_wiki,
         :manage_builds,
+        :download_build_artifacts,
         :push_code
       ]
     end
@@ -231,9 +232,10 @@ class Ability
 
       # Only group masters and group owners can create new projects in group
       if group.has_master?(user) || group.has_owner?(user) || user.admin?
-        rules.push(*[
-                     :create_projects,
-                   ])
+        rules += [
+          :create_projects,
+          :admin_milestones
+        ]
       end
 
       # Only group owner and administrators can admin group
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 05430c2ee18e106bdfa0f7a34900d518c83a3868..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
@@ -68,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
@@ -87,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 b19e2ac13634566ef3f7a9e05bd32f5904c38e3a..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
@@ -106,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 || ''
@@ -209,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)
@@ -222,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
@@ -240,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 13437b2483f1f6a6f9fef45227419c196bd28c75..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
@@ -187,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
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 4e806ca1a68eb5943739a403185841291fc4e231..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}").
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index b719ad3c87e987f7a5c828a607931b1edd9efe68..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)
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_status.rb b/app/models/commit_status.rb
index 0b73ab6d2eb591fe4d030c98e5fbefb4a2f95f76..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,8 +48,8 @@ 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) }
@@ -92,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 5e964f04ef55a39c45212bbbaa01f9ecd575475a..492a026add9ffdc2fd01420216a970adc850aa92 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -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) }
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 76%
rename from app/models/group_milestone.rb
rename to app/models/global_milestone.rb
index 91844da62e22c6c7a2b3cfc1f799e301f899fd9c..1321ccd963fb449491967fe1365c32394899e56a 100644
--- a/app/models/group_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -1,7 +1,15 @@
-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
@@ -10,7 +18,7 @@ class GroupMilestone
   def safe_title
     @title.parameterize
   end
-  
+
   def projects
     milestones.map { |milestone| milestone.project }
   end
@@ -60,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
@@ -86,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 9503d8b0f1c697fb78324bc491cf0566ff4d833b..11fde7ba6cd3a0802e14a0bdc0dc600a88cc5733 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'
@@ -98,7 +99,7 @@ class Group < Namespace
   end
 
   def public_profile?
-    projects.public_only.any?
+    self.public || projects.public_only.any?
   end
 
   def post_create_hook
diff --git a/app/models/group_label.rb b/app/models/group_label.rb
deleted file mode 100644
index 0fc39cb87716245bbcd36c658f92b2f0eb5e0e37..0000000000000000000000000000000000000000
--- a/app/models/group_label.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class GroupLabel
-  attr_accessor :title, :labels
-  alias_attribute :name, :title
-
-  def initialize(title, labels)
-    @title = title
-    @labels = labels
-  end
-end
diff --git a/app/models/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/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 85f37e49e625faed80368f481672782d36dd0b7a..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, 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
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 6575d0bc81f1ea118a37a2f9c7bc8f4191ef2b98..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
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/project.rb b/app/models/project.rb
index 79b7a6457d7dccea0c5475a493f980c1b5ebe90f..09465775786003cf0e103c60bcefbbc303700dbf 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
@@ -52,6 +53,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
@@ -123,6 +125,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
@@ -249,7 +254,7 @@ class Project < ActiveRecord::Base
         joins(:namespace).
         iwhere('namespaces.path' => namespace_path)
 
-      projects.where('projects.path' => project_path).take || 
+      projects.where('projects.path' => project_path).take ||
         projects.iwhere('projects.path' => project_path).take
     end
 
@@ -457,10 +462,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
@@ -777,12 +778,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 c9b36bd81705e6a6a177174bace99e3701b01c2a..f76b770e867d3ab8754fcdb0b84896d8d570021b 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -87,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
@@ -287,7 +296,7 @@ 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
@@ -337,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)
@@ -354,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
@@ -496,7 +496,7 @@ class Repository
 
   def search_files(query, ref)
     offset = 2
-    args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} -e #{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
 
@@ -528,7 +528,7 @@ class Repository
   end
 
   def fetch_ref(source_path, source_ref, target_ref)
-    args = %W(git fetch -f #{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 c72beacbf0fe2f374636fb2ba4293134b3775336..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)
 #
 
@@ -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
+      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;'
 
-      # 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
+      User.find_by_sql([sql, { email: email }]).first
     end
 
     def filter(filter_name)
@@ -409,6 +405,15 @@ class User < ActiveRecord::Base
     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 ||= Project.where(id: authorized_projects_id)
@@ -769,14 +774,10 @@ class User < ActiveRecord::Base
     !solo_owned_groups.present?
   end
 
-  def ci_authorized_projects
-    @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects_id)
-  end
-
   def ci_authorized_runners
     @ci_authorized_runners ||= begin
       runner_ids = Ci::RunnerProject.joins(:project).
-        where(ci_projects: { gitlab_id: authorized_projects_id }).select(:runner_id)
+        where(ci_projects: { gitlab_id: master_or_owner_projects_id }).select(:runner_id)
       Ci::Runner.specific.where(id: runner_ids)
     end
   end
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/git_push_service.rb b/app/services/git_push_service.rb
index 3de7bb9dcaaa80f2ef3b6f5baf00e10c0f573e0b..ccb6b97858c0f0023432b61790f608d7f9dd4cf1 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -60,7 +60,7 @@ class GitPushService
 
     # 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
 
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 551325e4cab43d4ed9bee2630b348b267aa523fb..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
-
-        if issue.previous_changes.include?('assignee_id')
-          create_assignee_note(issue)
-          notification_service.reassigned_issue(issue, current_user)
-        end
-
-        if issue.previous_changes.include?('title')
-          create_title_change_note(issue, issue.previous_changes['title'].first)
-        end
-
+        handle_changes(issue)
         issue.create_new_cross_references!(current_user)
         execute_hooks(issue, 'update')
       end
 
       issue
     end
+
+    def handle_changes(issue)
+      if issue.previous_changes.include?('milestone_id')
+        create_milestone_note(issue)
+      end
+
+      if issue.previous_changes.include?('assignee_id')
+        create_assignee_note(issue)
+        notification_service.reassigned_issue(issue, current_user)
+      end
+
+      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/labels/group_service.rb b/app/services/labels/group_service.rb
deleted file mode 100644
index b26cee24d56bbd1ab330744e74cc0b558a2f6fc8..0000000000000000000000000000000000000000
--- a/app/services/labels/group_service.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module Labels
-  class GroupService < ::BaseService
-    def initialize(project_labels)
-      @project_labels = project_labels.group_by(&:title)
-    end
-
-    def execute
-      build(@project_labels)
-    end
-
-    def label(title)
-      if title
-        group_label = @project_labels[title].group_by(&:title)
-        build(group_label).first
-      else
-        nil
-      end
-    end
-
-    private
-
-    def build(label)
-      label.map { |title, labels| GroupLabel.new(title, labels) }
-    end
-  end
-end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index d68bc79ecc064fe5134aaf5def2ad1e07190df4e..e180edb4bf3b2f7609748cc36c05ac06a44de2fb 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -7,17 +7,17 @@ module MergeRequests
       @branch_name = Gitlab::Git.ref_name(ref)
 
       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
 
       # Leave a system note if a branch was deleted/added
       if branch_added? || branch_removed?
         comment_mr_branch_presence_changed
-        comment_mr_with_commits
-      else
-        comment_mr_with_commits
-        close_merge_requests
       end
 
+      comment_mr_with_commits
       execute_mr_web_hooks
 
       true
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 61f7d2bbe895fc3d197c46a2b23320bb6aca2ce9..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!(current_user)
-        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/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/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/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 7a78526e09ac1903279b94ace7e17d0d4d241c8d..ddaf0e0e8ffae6b79185aa49caa2f9a9a6e55d98 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -130,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/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/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/admin/users/_profile.html.haml b/app/views/admin/users/_profile.html.haml
index 90d9980c85ca44e9a127a9378f6b7279d5e22694..7d11edc79e25147e6011f02706d36a002a58a377 100644
--- a/app/views/admin/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/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/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 2fe14c6388c3255b6e014400d45ddbb1f75d65ad..83077a398bd2fdefa855fc812de5c8c4bda2163f 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -1,14 +1,14 @@
-- 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.
 
@@ -22,7 +22,7 @@
         %th Open issues
         %th State
         %th Due date
-    - @dashboard_milestone.milestones.each do |milestone|
+    - @milestone.milestones.each do |milestone|
       %tr
         %td
           = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
@@ -39,46 +39,46 @@
 .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/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 a92ad5d751b985815ddcf3dadae39dacb1caea5c..d161259e4aa0d9714367116642d78d60df5699a8 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,22 +1,22 @@
-- 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.
 
@@ -30,7 +30,7 @@
         %th Open issues
         %th State
         %th Due date
-    - @group_milestone.milestones.each do |milestone|
+    - @milestone.milestones.each do |milestone|
       %tr
         %td
           = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
@@ -47,46 +47,46 @@
 .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 30bcdb8682775708840fc86826021e32b3808985..1f09a27e2d6d280b7bf7cc9d5eb1bb76b0f8bb59 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -66,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 a701e49ac56ea178be31daab235dfce6094a661f..bc3c90294e3bcb0afc4e6180bad67a20210843ee 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -22,7 +22,7 @@
       %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-holder
@@ -46,5 +46,5 @@
   .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 beca6ab14232bc59d55569643bf35e8a58edb857..b902006597bd64239ba9fffa917dd044bbce21e8 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -48,5 +48,5 @@
           %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 0669b05adca9da10e1ffd49fbfc6cae15e147c11..0699321c8c00ac1cf624af837b78d9631cb96ba8 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -43,5 +43,5 @@
           %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 3bc85059e7d926a5375cee54a7d1fbb5659c1988..f4a2b33af21fa9d851f392c9bac0ba364072a85f 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -43,5 +43,5 @@
           %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 2e3a535737f0172e0c75694fa59873aceeff964b..71752d21efab67c3722cd75274826d51574d8526 100644
--- a/app/views/import/gitorious/status.html.haml
+++ b/app/views/import/gitorious/status.html.haml
@@ -43,5 +43,5 @@
           %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 c5af06edf8705bc382f0dc0814574c7d02bc834d..8c64fd27e604373ce0d2d5b12dd7f20380c8e7a4 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -67,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/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 356ce09c3d74c1a29ab01b4018da9f11bf4140a8..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')
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 854cda57c39638646c04059d57b133cb020be69e..3ca4c340406511d7488e76997834f7dd75e0aa64 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -40,9 +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}
+            #{link_to "View it on GitLab", @target_url}.
           %br
-          You're receiving this email because of your account on #{link_to Gitlab.config.gitlab.host, root_url}. 
+          -# 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
+          = email_action @target_url
\ No newline at end of file
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/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/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 012858f70b4fae6b403c020c3497ce1cdc934136..101880bd1053451877c8d31521562dd63a5a6ffa 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -8,5 +8,5 @@
 .content_list{:"data-href" => activity_project_path(@project)}
 = spinner
 
-:coffeescript
-  new Activities()
+:javascript
+  new Activities();
diff --git a/app/views/projects/_last_commit.html.haml b/app/views/projects/_last_commit.html.haml
index d7b20bfc6b113d75ce1cccd8dd2df7e4f0fa489c..7e1ee2b7fc1ab92aa0c46a0f60d45528a016e883 100644
--- a/app/views/projects/_last_commit.html.haml
+++ b/app/views/projects/_last_commit.html.haml
@@ -6,7 +6,7 @@
       = 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.id), class: "commit-row-message"
+  = 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/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/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 e08556673ed6eac3c370502d99113756732dcc19..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,7 +25,7 @@
         %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?
@@ -40,14 +40,14 @@
             %th Build ID
             %th Commit
             %th Ref
-            %th Runner
+            %th Stage
             %th Name
             %th Duration
             %th Finished at
             %th
 
         - @builds.each do |build|
-          = render 'projects/builds/build', build: build
+          = render 'projects/commit_statuses/commit_status', commit_status: build, commit_sha: true, stage: true, allow_retry: true
 
-    = paginate @builds
+    = paginate @builds, theme: 'gitlab'
 
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index e3d8d7349135fa85c468e3f25c2620020baab236..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(:id), "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_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/_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 c164b2d4bc03836586769a3286b74cd1ba1904a1..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
 
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/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 369086b39ed0d59d393c93602258bd8e8178e307..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
 
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/ci.html.haml b/app/views/projects/commit/builds.html.haml
similarity index 76%
rename from app/views/projects/commit/ci.html.haml
rename to app/views/projects/commit/builds.html.haml
index 43033cad24c97ef4150273faae4caea68b021b07..00cf9c761025df9d9aee80c697f969e27510a6ca 100644
--- a/app/views/projects/commit/ci.html.haml
+++ b/app/views/projects/commit/builds.html.haml
@@ -1,4 +1,4 @@
-- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
+- page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits"
 = render "projects/commits/header_title"
 = render "commit_box"
 = render "ci_menu"
@@ -26,8 +26,11 @@
     &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 all", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-danger'
+        = 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
@@ -45,7 +48,7 @@
         %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 }
+               locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true }
 
 - if @ci_commit.retried.any?
   .gray-content-block.second-block
@@ -66,4 +69,4 @@
             %th Coverage
           %th
       = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried,
-               locals: { coverage: @ci_project.try(:coverage_enabled?) }
+               locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true }
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/_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 56b51f038bab268dcb51df6d3055a536a227e2f7..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
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index afbf88b55073ed9bdcb37b348c3d345e5b18aa80..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
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/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 300a37152926c3150411ce3c03fa098e3199be12..c5234c0618c4d06250ad6b6032cae58cb8bcb0c0 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -41,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/_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/_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/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index a3551516bfe5c9a1cd06abbc3d15a4545d3aa31b..ba5ad22bca750a83285150c78bb4ee6f2e8a3db4 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -38,6 +38,7 @@
       = 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..8a3292f7daf0f0e57b6a24e79cb283bb54934272 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -2,12 +2,12 @@
   .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)
           .note-actions
-            = link_to '#', title: 'Edit comment', class: 'js-note-edit' do
+            = link_to edit_namespace_project_note_path(note.project.namespace, note.project, note), title: 'Edit comment', remote: true, class: 'js-note-edit' do
               = icon('pencil-square-o')
 
             = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'js-note-delete danger' do
@@ -59,9 +59,6 @@
         .note-text
           = preserve do
             = markdown(note.note, {no_header_anchors: true})
-        - unless note.system?
-          -# System notes can't be edited
-          = render 'projects/notes/edit_form', note: note
 
       - if note.attachment.url
         .note-attachment
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 04222b8f7c47e10d7e2d3922c3235a6fd6c2bde9..91cefa6d14d8ac5fda41504bb2a8affc41cf1bad 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -7,4 +7,4 @@
   = render "projects/notes/form", view: params[:view]
 
 :javascript
-  new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}")
+  window._notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}")
diff --git a/app/views/projects/notes/edit.js.erb b/app/views/projects/notes/edit.js.erb
new file mode 100644
index 0000000000000000000000000000000000000000..2599bad5d6e107fdef70efa7febf245437b69b31
--- /dev/null
+++ b/app/views/projects/notes/edit.js.erb
@@ -0,0 +1,2 @@
+$note = $('.note-row-<%= @note.id %>:visible');
+_notes.showEditForm($note, '<%= escape_javascript(render('edit_form', note: @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/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/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 c255cd51bd2969e01d30472c5d89350861e12405..5bf4c09ca25e09e548fa3add532f4f57b36cb4c9 100644
--- a/app/views/projects/runners/show.html.haml
+++ b/app/views/projects/runners/show.html.haml
@@ -1,13 +1,14 @@
-= 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"
+
+%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
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/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml
index 18a37302c3e5b254fb99ba31a3b74cbdb774d5bf..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
 
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/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/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 0e4e9c0987afaac9dc331a176145a15f7590a648..d1231438ee4df03c2f04743dbce48957cdb737be 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -60,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/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
index 751fafa8942c080af20974b4b3f720dc0aa358a5..1979ae6d5bc473ebf218d9633bee889a747408cc 100644
--- a/app/views/snippets/_actions.html.haml
+++ b/app/views/snippets/_actions.html.haml
@@ -1,11 +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, :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
 - 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/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/show.html.haml b/app/views/users/show.html.haml
index 4ea4a1f92c2ddfb188f3c1a918549d1a60fd9eaa..d5a92cb816adf83ea7e507a43a91ca97b41c855d 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -24,22 +24,27 @@
 
   .cover-desc
     - unless @user.public_email.blank?
-      = link_to @user.public_email, "mailto:#{@user.public_email}"
+      .profile-link-holder
+        = link_to @user.public_email, "mailto:#{@user.public_email}"
     - unless @user.skype.blank?
-      &middot;
-      = link_to "Skype", "skype:#{@user.skype}"
+      .profile-link-holder
+        = link_to "skype:#{@user.skype}", title: "Skype" do
+          = icon('skype')
     - unless @user.linkedin.blank?
-      &middot;
-      = link_to "LinkedIn", "http://www.linkedin.com/in/#{@user.linkedin}"
+      .profile-link-holder
+        = link_to "https://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do
+          = icon('linkedin-square')
     - unless @user.twitter.blank?
-      &middot;
-      = link_to "Twitter", "http://www.twitter.com/#{@user.twitter}"
+      .profile-link-holder
+        = link_to "https://twitter.com/#{@user.twitter}", title: "Twitter" do
+          = icon('twitter-square')
     - unless @user.website_url.blank?
-      &middot;
-      = link_to @user.short_website_url, @user.full_website_url
+      .profile-link-holder
+        = link_to @user.short_website_url, @user.full_website_url
     - unless @user.location.blank?
-      &middot;
-      = @user.location
+      .profile-link-holder
+        = icon('map-marker')
+        = @user.location
 
 
   .cover-controls
@@ -47,7 +52,7 @@
       = link_to profile_path, class: 'btn btn-gray' do
         = icon('pencil')
     - elsif current_user
-      .report-abuse
+      %span.report-abuse
         - if @user.abuse_report
           %button.btn.btn-danger{ title: 'Already reported for abuse',
             data: { toggle: 'tooltip', placement: 'left', container: 'body' }}
@@ -56,6 +61,10 @@
           = 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')
 
 .gray-content-block.second-block
   .user-calendar
@@ -64,27 +73,47 @@
   .user-calendar-activities
 
 
-.row.prepend-top-20
-  %section.col-md-7
-    - if @groups.any?
-      .prepend-top-20
-        %h4 Groups
-        = render 'groups', groups: @groups
-        %hr
-
-    %h4
-      User Activity
-
-      - 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
+%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 '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/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 e297f393e3d9d046ba17671d50b54d0237efdfbe..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: {
@@ -315,8 +323,6 @@ production: &base
     # path: /mnt/gitlab # Default: shared
 
 
-
-
   #
   # 4. Advanced settings
   # ==========================
@@ -417,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 c189c88bdcba67fbab94444fff0060cffbb481bd..6b7990c0ab06f8f703bf5e57de35ca18bf6cc9c9 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -171,6 +171,7 @@ 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.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
 Settings.gitlab['restricted_signup_domains'] ||= []
@@ -181,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
@@ -192,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
 #
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 0458f538eb6f8b9ce50e08c2774ead42ec61f950..c0077ab1a997759cf1bcf3855f3a06f250b434fe 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,7 +32,6 @@ 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]
@@ -81,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'
@@ -210,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
@@ -219,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
@@ -354,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
 
@@ -472,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
 
@@ -569,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]
@@ -588,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
@@ -641,7 +660,7 @@ Gitlab::Application.routes.draw do
           end
         end
 
-        resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do
+        resources :notes, constraints: { id: /\d+/ } do
           member do
             delete :delete_attachment
           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/20151019111551_fix_build_tags.rb b/db/migrate/20151019111551_fix_build_tags.rb
index 84b142183f8a7c60a14c3c8c6a15315750299cf0..299a24b0a7c5a75ce171f4cd5bf5765ee0336ac2 100644
--- a/db/migrate/20151019111551_fix_build_tags.rb
+++ b/db/migrate/20151019111551_fix_build_tags.rb
@@ -1,5 +1,9 @@
 class FixBuildTags < ActiveRecord::Migration
-  def change
+  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
index 546b03d81295d0c0fba62eaf67615d14dcd3d8a5..dcdb5d1b25ddcd5adb0744ce9d143b8814d4ea02 100644
--- a/db/migrate/20151019111703_fail_build_without_names.rb
+++ b/db/migrate/20151019111703_fail_build_without_names.rb
@@ -1,5 +1,8 @@
 class FailBuildWithoutNames < ActiveRecord::Migration
-  def change
+  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/20151023112551_fail_build_with_empty_name.rb b/db/migrate/20151023112551_fail_build_with_empty_name.rb
index f069bc60ac7242b771e193a0b85f07728470d34f..41c0f0649cd6192211e9a219abf24eba2f562fd0 100644
--- a/db/migrate/20151023112551_fail_build_with_empty_name.rb
+++ b/db/migrate/20151023112551_fail_build_with_empty_name.rb
@@ -1,5 +1,8 @@
 class FailBuildWithEmptyName < ActiveRecord::Migration
-  def change
+  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/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/schema.rb b/db/schema.rb
index 4bde9f0b7482d13c251bbabd24f46f8b9286e2ce..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: 20151026182941) do
+ActiveRecord::Schema.define(version: 20151116144118) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -47,6 +47,8 @@ ActiveRecord::Schema.define(version: 20151026182941) do
     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|
@@ -107,6 +109,7 @@ ActiveRecord::Schema.define(version: 20151026182941) 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
@@ -419,6 +422,25 @@ ActiveRecord::Schema.define(version: 20151026182941) 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
@@ -501,14 +523,15 @@ ActiveRecord::Schema.define(version: 20151026182941) 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
@@ -637,6 +660,17 @@ ActiveRecord::Schema.define(version: 20151026182941) 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"
@@ -667,6 +701,7 @@ ActiveRecord::Schema.define(version: 20151026182941) 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/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 ea8f72bc1358e38831d50c6796e63c3c58391fee..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.
@@ -141,6 +155,8 @@ job_name:
 | 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`.
@@ -169,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.
@@ -182,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.
 
@@ -246,6 +274,86 @@ 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`.
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/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 b48acb655eee8157a77bc08c4c89096de129fc44..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-1-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-1-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
@@ -320,16 +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 checkout 0.3.0
+    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.
@@ -489,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/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/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 606532a6fbe91892c4dac7d4e0f3ae826783ce7f..1a5442cdac71838425065f70724fd469b7020010 100644
--- a/doc/raketasks/backup_restore.md
+++ b/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, builds(CI build output logs). 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/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/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 9a24a1e252acef81ecc3213d23c60517e83b0969..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.
@@ -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’re 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 7777c0298a449253554bef90e4902e6790b1b92e..abf3769a84446c7355cf924a30abe0b2549892a2 100644
--- a/features/groups.feature
+++ b/features/groups.feature
@@ -161,6 +161,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
@@ -177,4 +184,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 f423c3ba542058fe2aad79bb60f24184adabf88a..6cd081c868e6582c66be1a40de7a032c1def9b4b 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -16,6 +16,15 @@ Feature: Project Merge Requests
     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/steps/groups.rb b/features/steps/groups.rb
index 5fd4dea5cd651c0501003226df3d2e6b99f72c98..9c0313537b12db5573d86d1e956494dfc34e543c 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
@@ -273,6 +273,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/issues/issues.rb b/features/steps/project/issues/issues.rb
index af2da41badb220f0fa07e965fc9d079242a43d96..be134b9c2bb786c7dca6b8b087352c4354940551 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -219,7 +219,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
   end
 
   step 'The code block should be unchanged' do
-    expect(page).to have_content("```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```")
+    expect(page).to have_content("Command [1]: /usr/local/bin/git , see [text](doc/text)")
   end
 
   step 'project \'Shop\' has issue \'Bugfix1\' with description: \'Description for issue1\'' do
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 92ec14d0d76cdb81ddcd8301d4a5c26f60d47e20..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",
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index d76891d5bdebb426d8ace148e69bee65cf2aafa8..9ca7c8ebbc7ef8ed8efcbeb3f7f137d14e16c5eb 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -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/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_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/lib/api/api.rb b/lib/api/api.rb
index 40671e2517c16c97fa627c437eb72dd12dbe4d7e..fe1bf8a48160c1fd4fe244925d227e8ae9db9597 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -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 652bdf9b2781b91d6fed42db5e4c716a67014b4b..92540ccf2b106bb09cbaba7a85972e21dabfa20e 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -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 67ee66a2058a2be885df8cfe1d820a922d9060b5..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,
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 0a4cbf69b63780cea701c59f2cf0b3db4e1f6de2..07e68216d7f1d08876f09545cd426bac8f787323 100644
--- a/lib/ci/api/api.rb
+++ b/lib/ci/api/api.rb
@@ -27,6 +27,7 @@ module Ci
 
       helpers Helpers
       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 efcd2faffc74b143ffb1ad12904160195585fe1a..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, :when]
+    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]),
@@ -96,19 +78,13 @@ module Ci
         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")
@@ -138,6 +114,16 @@ 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)
       end
@@ -186,7 +172,27 @@ module Ci
         raise ValidationError, "#{name} job: except parameter should be an array of strings"
       end
 
-      if job[:allow_failure] && !job[:allow_failure].in?([true, false])
+      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
 
@@ -208,5 +214,40 @@ module Ci
     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/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 85a2d1a93a7dc0f32db7107419db078d34af19df..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"
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/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/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/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 0a2be605af9bf656505d00e6602c0c161d3ec8cd..70de6a74e767a928fde2549f9582f22135642bdf 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -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/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/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 2e73f792a9da901baf6f06a911cac14941a90d8a..a25fac62cfc1a392158574aca60d70669dfb9977 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -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/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/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/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index fcbe62cace85c9f243e489679fb82b486d8fc488..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) }
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/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/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/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index d7cb3b2e86ead6487a8e965f03168475274c9c96..16d5a03e88ca1c47ccab84f82380b4fa6ed6f4f2 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -65,12 +65,6 @@ describe 'Comments', feature: true do
     end
 
     describe 'when editing a note', js: true do
-      it 'should contain the hidden edit form' do
-        page.within("#note_#{note.id}") do
-          is_expected.to have_css('.note-edit-form', visible: false)
-        end
-      end
-
       describe 'editing the note' do
         before do
           find('.note').hover
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/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index fca3c77fc64464e9e8a1fda7d31c237b7c699902..6cbe685a93bf51af1245f96298c85278078eb473 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -51,7 +51,6 @@ feature 'Task Lists', feature: true do
 
       expect(page).to have_selector(container)
       expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox")
-      expect(page).to have_selector("#{container} .js-task-list-field")
       expect(page).to have_selector('form.js-issuable-update')
       expect(page).to have_selector('a.btn-close')
     end
@@ -90,7 +89,6 @@ feature 'Task Lists', feature: true do
 
       expect(page).to have_selector('.note .js-task-list-container')
       expect(page).to have_selector('.note .js-task-list-container .task-list .task-list-item .task-list-item-checkbox')
-      expect(page).to have_selector('.note .js-task-list-container .js-task-list-field')
     end
 
     it 'is only editable by author' do
@@ -127,7 +125,6 @@ feature 'Task Lists', feature: true do
 
       expect(page).to have_selector(container)
       expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox")
-      expect(page).to have_selector("#{container} .js-task-list-field")
       expect(page).to have_selector('form.js-issuable-update')
       expect(page).to have_selector('a.btn-close')
     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/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 abdb6b89ac55e592e7dd7cd4885a0eedcb5fdefd..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({
@@ -28,78 +29,218 @@ module Ci
           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
+
+        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"] },
+                             })
 
-        expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+          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$/"] }
+                             })
+
+          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("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)
+          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
@@ -111,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({
@@ -139,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({
@@ -172,7 +313,7 @@ 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
@@ -184,7 +325,7 @@ module Ci
                                rspec: { script: "rspec", when: when_state }
                              })
 
-          config_processor = GitlabCiYamlProcessor.new(config)
+          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)
@@ -192,6 +333,97 @@ module Ci
       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)
@@ -200,156 +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)
+          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)
+          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)
+          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/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/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/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/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 44dbd083f064cc193c9e056977e0d9dcaf048310..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'
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/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/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 eed2cbc54128104814664888ca94702bdbb07a73..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'
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/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 e9de9e0826dc109fd71b71a196a5c08e009e8ef8..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
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/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/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/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 227ac995ec2ca4237b96a90cdf9f1adce7d2a78b..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
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/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