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..39ff95c50ee64fa21bb7bf6dc9d28126da616d71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@ nohup.out
 public/assets/
 public/uploads.*
 public/uploads/
+shared/artifacts/
 rails_best_practices_output.html
 /tags
 tmp/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cf6d28b01af06af94652b4ee11f42d007fcc7975..128faa07db8420421daa85e3b3bf31c55998393c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -73,3 +73,19 @@ brakeman:
   tags:
     - ruby
     - mysql
+
+flog:
+  script:
+    - bundle exec rake flog
+  tags:
+    - ruby
+    - mysql
+  allow_failure: true
+
+flay:
+  script:
+    - bundle exec rake flay
+  tags:
+    - ruby
+    - mysql
+  allow_failure: true
diff --git a/CHANGELOG b/CHANGELOG
index 412bdafc2b2bf45334a54788fbbe5672986a8c53..c0257cc8a2f22a59c22b63d4dc8641e77460061e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,8 +1,12 @@
 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)
+  - 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
@@ -13,13 +17,38 @@ 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
   - 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.
   - Add graphs of commits ahead and behind default branch (Jeff Stubler)
   - [API] Add ability to fetch the commit ID of the last commit that actually touched a file
+  - Add "New file" link to dropdown on project page
+  - Include commit logs in project search
+  - Add "added", "modified" and "removed" properties to commit object in webhook
+  - Rename "Back to" links to "Go to" because its not always a case it point to place user come from
+  - Allow groups to appear in the search results if the group owner allows it
+  - New design for project graphs page
+  - Fix incoming email config defaults
+  - MR target branch is now visible on a list view when it is different from project's default one
+  - Improve Continuous Integration graphs page
+  - Make color of "Accept Merge Request" button consistent with current build status
+
+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
@@ -29,13 +58,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.
@@ -73,6 +104,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/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/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..951173af5d5bef3dbed97fb6a277d6f1e7e04d5c 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -39,6 +39,12 @@ class Dispatcher
         shortcut_handler = new ShortcutsNavigation()
         new DropzoneInput($('.merge-request-form'))
         new IssuableForm($('.merge-request-form'))
+      when 'projects:tags:new'
+        new ZenMode()
+        new DropzoneInput($('.tag-form'))
+      when 'projects:releases:edit'
+        new ZenMode()
+        new DropzoneInput($('.release-form'))
       when 'projects:merge_requests:show'
         new Diff()
         shortcut_handler = new ShortcutsIssuable()
diff --git a/app/assets/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/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 4a080db7464b3d66c29179b7836c71442df43b6e..879bd287470041aec774186a4f1d3f2025449e74 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;
@@ -114,6 +116,13 @@ li.commit {
   }
 }
 
+.branch-commit {
+  color: $gl-gray;
+  .commit-id, .commit-row-message {
+    color: $gl-gray;
+  }
+}
+
 .divergence-graph {
   padding: 12px 12px 0 0;
   float: right;
@@ -169,6 +178,3 @@ li.commit {
     background-color: #ccc;
   }
 }
-
-
-
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_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/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/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/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/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 16c42386623347e43ec941e93b1ca138b6c7fa0d..b0788a2d073e8ceca450aa50872cecd50cadcfb6 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -31,6 +31,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     end
 
     @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
+    @merge_requests = @merge_requests.preload(:target_project)
 
     respond_to do |format|
       format.html
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..00d13a83ce8372b44a099389564baa534be8a981 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]
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/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/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 b72178fa12608f84058cb356ee71d7c798e8ebc4..5ae28d5133e63296f885e26c6d72ed3c038ebebc 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -154,6 +154,7 @@ class Ability
         :create_merge_request,
         :create_wiki,
         :manage_builds,
+        :download_build_artifacts,
         :push_code
       ]
     end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 05430c2ee18e106bdfa0f7a34900d518c83a3868..fa7cf2464ad7b6771faaa1f6d9fbb2fba196d5bd 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -87,7 +87,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/build.rb b/app/models/ci/build.rb
index b19e2ac13634566ef3f7a9e05bd32f5904c38e3a..0ec7e210321169ff7d2ce08a1ce60853047d5580 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -39,6 +39,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 +108,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 +219,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 +240,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 +258,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..e58420d82d462268fc3ffa3a92659bc58766ecac 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -187,7 +187,7 @@ module Ci
     end
 
     def config_processor
-      @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file)
+      @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace)
     rescue Ci::GitlabCiYamlProcessor::ValidationError => e
       save_yaml_error(e.message)
       nil
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 0b73ab6d2eb591fe4d030c98e5fbefb4a2f95f76..d346c5d35d21264f1f275c70fd828870fac56bc3 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -15,8 +15,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 +92,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/group.rb b/app/models/group.rb
index 465c22d23ac85ae8305112bf9f637fac3693f336..34904af3b5bde7dd1a7f7f9da8dac0d4e4b128c6 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -120,7 +120,7 @@ class Group < Namespace
   end
 
   def public_profile?
-    projects.public_only.any?
+    self.public || projects.public_only.any?
   end
 
   def post_create_hook
diff --git a/app/models/project.rb b/app/models/project.rb
index e73a856c2891c6fe7c10ef0b5616eb7a9c83d32d..a01246a4c7300998aba2d6feb16821bc4b5205ad 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
@@ -121,6 +122,7 @@ 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_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
   has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id
@@ -247,7 +249,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
 
@@ -777,7 +779,9 @@ class Project < ActiveRecord::Base
   end
 
   def ensure_gitlab_ci_project
-    gitlab_ci_project || create_gitlab_ci_project
+    gitlab_ci_project || create_gitlab_ci_project(
+      shared_runners_enabled: current_application_settings.shared_runners_enabled
+    )
   end
 
   def enable_ci
diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb
index cbf325cc5255af0193bbb25155db43de7fe91767..d89466b689f8aeda158c3ea655bf8d5b4f8a6a33 100644
--- a/app/models/project_services/ci/hip_chat_message.rb
+++ b/app/models/project_services/ci/hip_chat_message.rb
@@ -11,7 +11,7 @@ module Ci
     def to_s
       lines = Array.new
       lines.push("<a href=\"#{ci_project_url(project)}\">#{project.name}</a> - ")
-      lines.push("<a href=\"#{ci_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}\">Commit ##{commit.id}</a></br>")
+      lines.push("<a href=\"#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}\">Commit ##{commit.id}</a></br>")
       lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}</br>")
       lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).")
       lines.join('')
diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb
index dc050a3fc59973a8890e5f6e09daa833af3931e4..1a6ff8e34c98af15048640b6b9a6bab7fcad0f2b 100644
--- a/app/models/project_services/ci/slack_message.rb
+++ b/app/models/project_services/ci/slack_message.rb
@@ -45,7 +45,7 @@ module Ci
 
     def attachment_message
       out = "<#{ci_project_url(project)}|#{project_name}>: "
-      out << "Commit <#{ci_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> "
+      out << "Commit <#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> "
       out << "(<#{commit_sha_link}|#{commit.short_sha}>) "
       out << "of <#{commit_ref_link}|#{commit.ref}> "
       out << "by #{commit.git_author_name} " if commit.git_author_name
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index c73c4b058a1bf8fc248368932c96ca22ba4d3afb..c240213200ddb077006042f0c9c17d18e8154daf 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -32,7 +32,6 @@ class DroneCiService < CiService
 
   def compose_service_hook
     hook = service_hook || build_service_hook
-    hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join
     hook.enable_ssl_verification = enable_ssl_verification
     hook.save
   end
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 4dcd16ede3a29d5cb835b51860177747068b316c..095d04e0df4c0a2959e796d849ab583cdfa487c8 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -71,7 +71,7 @@ class GitlabCiService < CiService
 
   def build_page(sha, ref)
     if project.gitlab_ci_project.present?
-      ci_namespace_project_commit_url(project.namespace, project, sha)
+      builds_namespace_project_commit_url(project.namespace, project, sha)
     end
   end
 
diff --git a/app/models/release.rb b/app/models/release.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e196b84eb18845cbbfdf8b2acedd6309ab3931f2
--- /dev/null
+++ b/app/models/release.rb
@@ -0,0 +1,5 @@
+class Release < ActiveRecord::Base
+  belongs_to :project
+
+  validates :description, :project, :tag, presence: true
+end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 0e2d4ea1fb8fbf8fe357ebeed5effd6c98300380..2ba6a627e461802c2ed982fe778d8e8bebf8de5f 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
@@ -321,7 +330,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
@@ -372,7 +381,7 @@ class Repository
   end
 
   def branch_names_contains(sha)
-    args = %W(git branch --contains #{sha})
+    args = %W(#{Gitlab.config.git.bin_path} branch --contains #{sha})
     names = Gitlab::Popen.popen(args, path_to_repo).first
 
     if names.respond_to?(:split)
@@ -389,7 +398,7 @@ class Repository
   end
 
   def tag_names_contains(sha)
-    args = %W(git tag --contains #{sha})
+    args = %W(#{Gitlab.config.git.bin_path} tag --contains #{sha})
     names = Gitlab::Popen.popen(args, path_to_repo).first
 
     if names.respond_to?(:split)
@@ -530,7 +539,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
 
@@ -562,7 +571,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/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/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/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/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/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/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/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index 41ad2c231d4d1cfaa0c2d3478d2e185ca579dcbb..9a1331b2549361fbb5b1ed389b6e702bdc4a761e 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -7,26 +7,34 @@
       %h3 Sign in
   .login-body
     - if form_based_providers.any?
-      %ul.nav.nav-tabs
+      - if form_based_providers.count >= 2 || signin_enabled?
+        %ul.nav.nav-tabs
+          - if crowd_enabled?
+            %li.active
+              = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
+          - @ldap_servers.each_with_index do |server, i|
+            %li{class: (:active if i.zero? && !crowd_enabled?)}
+              = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
+          - if signin_enabled?
+            %li
+              = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
+        .tab-content
+          - if crowd_enabled?
+            %div.tab-pane.active{id: "tab-crowd"}
+              = render 'devise/sessions/new_crowd'
+          - @ldap_servers.each_with_index do |server, i|
+            %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)}
+              = render 'devise/sessions/new_ldap', server: server
+          - if signin_enabled?
+            %div#tab-signin.tab-pane
+              = render 'devise/sessions/new_base'
+      - else
         - if crowd_enabled?
-          %li.active
-            = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
-        - @ldap_servers.each_with_index do |server, i|
-          %li{class: (:active if i.zero? && !crowd_enabled?)}
-            = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
-        - if signin_enabled?
-          %li
-            = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
-      .tab-content
-        - if crowd_enabled?
-          %div.tab-pane.active{id: "tab-crowd"}
-            = render 'devise/sessions/new_crowd'
-        - @ldap_servers.each_with_index do |server, i|
-          %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)}
-            = render 'devise/sessions/new_ldap', server: server
-        - if signin_enabled?
-          %div#tab-signin.tab-pane
-            = render 'devise/sessions/new_base'
+          = render 'devise/sessions/new_crowd'
+        - elsif @ldap_servers.any?
+          = render 'devise/sessions/new_ldap', server: @ldap_servers.first
+        - elsif signin_enabled?
+          = render 'devise/sessions/new_base'
 
     - elsif signin_enabled?
       = render 'devise/sessions/new_base'
diff --git a/app/views/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/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..c08a7b8074474222551ef746e14cda1269c08bf8 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -21,6 +21,11 @@
             %li
               = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do
                 = icon('plus fw')
+          - if Gitlab::Sherlock.enabled?
+            %li
+              = link_to sherlock_transactions_path, title: 'Sherlock Transactions',
+                data: {toggle: 'tooltip', placement: 'bottom'} do
+                = icon('tachometer fw')
           %li
             = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do
               = icon('sign-out')
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index eb35af22b93836d1af2fb7052c99f2875ac4f39e..319352876b4ec648b8b1315ad1669790cc979430 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,9 +1,9 @@
 %ul.nav.nav-sidebar
   = nav_link do
-    = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do
+    = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
       = icon('caret-square-o-left fw')
       %span
-        Back to dashboard
+        Go to dashboard
 
   %li.separate-item
 
diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml
index 8075fe32fbce4b261630dafad8cc3b3b1d0cf372..c8411521f3692f40746882e3da47e8a8611e26e0 100644
--- a/app/views/layouts/nav/_group_settings.html.haml
+++ b/app/views/layouts/nav/_group_settings.html.haml
@@ -1,9 +1,9 @@
 %ul.nav.nav-sidebar
   = nav_link do
-    = link_to group_path(@group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do
+    = link_to group_path(@group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do
       = icon('caret-square-o-left fw')
       %span
-        Back to group
+        Go to group
 
   %li.separate-item
 
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 5a47b8e6db2ec0656e9526a5e474f09ef6246bce..0f3a793e30be18ac993bdc59ee800b60144ae48d 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,9 +1,9 @@
 %ul.nav.nav-sidebar
   = nav_link do
-    = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do
+    = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
       = icon('caret-square-o-left fw')
       %span
-        Back to dashboard
+        Go to dashboard
 
   %li.separate-item
 
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 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..a59939ccd31f5335652e8c161fdbca11057598af 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -1,9 +1,9 @@
 %ul.nav.nav-sidebar
   = nav_link do
-    = link_to project_path(@project), title: 'Back to project', data: {placement: 'right'}, class: 'back-link' do
+    = link_to project_path(@project), title: 'Go to project', data: {placement: 'right'}, class: 'back-link' do
       = icon('caret-square-o-left fw')
       %span
-        Back to project
+        Go to project
 
   %li.separate-item
 
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/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..7661452e6ec5cc140eb8f8771ad34232dc1f02c0 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,7 +162,9 @@
 
       - 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
@@ -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..bed2b16249e62aa888a33739f9f6344766450eda 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -20,6 +20,10 @@
             New snippet
       - if can?(current_user, :push_code, @project)
         %li.divider
+        %li
+          = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), title: 'New file' do
+            = icon('file fw')
+            New file
         %li
           = link_to new_namespace_project_branch_path(@project.namespace, @project) do
             = icon('code-fork fw')
diff --git a/app/views/projects/buttons/_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..665556f5c204c6404202f102c0c6f8332423d4f8 100644
--- a/app/views/projects/ci_settings/edit.html.haml
+++ b/app/views/projects/ci_settings/edit.html.haml
@@ -1,3 +1,4 @@
+- page_title "CI Settings"
 - if @ci_project.generated_yaml_config
   %p.alert.alert-danger
     CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_ci_project_path(@ci_project)}
diff --git a/app/views/projects/ci_web_hooks/index.html.haml b/app/views/projects/ci_web_hooks/index.html.haml
index 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/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/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index bbfaf422a82ea2be50dc9d37899492ebe0549435..e0d06a14bf44ee79d39d885ac4a21a341217c11d 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -1,4 +1,4 @@
-%ul.nav.nav-tabs
+%ul.center-top-menu
   = nav_link(action: :show) do
     = link_to 'Contributors', namespace_project_graph_path
   = nav_link(action: :commits) do
diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml
index 4f69cc64f7c9acf616984698ddd1c3672123a649..6fa77cc10c663d2c160e74ccada3ca980b48ab45 100644
--- a/app/views/projects/graphs/ci.html.haml
+++ b/app/views/projects/graphs/ci.html.haml
@@ -1,7 +1,16 @@
 - page_title "Continuous Integration", "Graphs"
 = render "header_title"
 = render 'head'
+.gray-content-block.append-bottom-default
+  .oneline
+    A collection of graphs for Continuous Integration
+
 #charts.ci-charts
+  .row
+    .col-md-6
+      = render 'projects/graphs/ci/overall'
+    .col-md-6
+      = render 'projects/graphs/ci/build_times'
+
+  %hr
   = render 'projects/graphs/ci/builds'
-  = render 'projects/graphs/ci/build_times'
-= render 'projects/graphs/ci/overall'
diff --git a/app/views/projects/graphs/ci/_build_times.haml b/app/views/projects/graphs/ci/_build_times.haml
index c3c2f5724148aa6e2b4af15ad6784882e00e2b1f..c58223fd39e644c66e6d9f71e9a9dd97ab2e82fa 100644
--- a/app/views/projects/graphs/ci/_build_times.haml
+++ b/app/views/projects/graphs/ci/_build_times.haml
@@ -1,21 +1,22 @@
-%fieldset
-  %legend
+%div
+  %p.light
     Commit duration in minutes for last 30 commits
 
-  %canvas#build_timesChart.padded{width: 800, height: 300}
+  %canvas#build_timesChart{height: 200}
 
 :javascript
   var data = {
     labels : #{@charts[:build_times].labels.to_json},
     datasets : [
       {
-        fillColor : "#4A3",
-        strokeColor : "rgba(151,187,205,1)",
-        pointColor : "rgba(151,187,205,1)",
-        pointStrokeColor : "#fff",
+        fillColor : "rgba(220,220,220,0.5)",
+        strokeColor : "rgba(220,220,220,1)",
+        barStrokeWidth: 1,
+        barValueSpacing: 1,
+        barDatasetSpacing: 1,
         data : #{@charts[:build_times].build_times.to_json}
       }
     ]
   }
   var ctx = $("#build_timesChart").get(0).getContext("2d");
-  new Chart(ctx).Line(data,{"scaleOverlay": true});
+  new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false});
diff --git a/app/views/projects/graphs/ci/_builds.haml b/app/views/projects/graphs/ci/_builds.haml
index 1b0039fb8346e6f98e31fb438ba324ddacb35c79..8fca07114fad7d8577930924c7d4c7b955c68ee7 100644
--- a/app/views/projects/graphs/ci/_builds.haml
+++ b/app/views/projects/graphs/ci/_builds.haml
@@ -1,20 +1,30 @@
-%fieldset
-  %legend
-    Builds chart for last week
-    (#{date_from_to(Date.today - 7.days, Date.today)})
+%h4 Build charts
+%p
+  &nbsp;
+  %span.cgreen
+    = icon("circle")
+    success
+  &nbsp;
+  %span.cgray
+    = icon("circle")
+    all
 
-  %canvas#weekChart.padded{width: 800, height: 200}
+.prepend-top-default
+  %p.light
+    Builds for last week
+    (#{date_from_to(Date.today - 7.days, Date.today)})
+  %canvas#weekChart{height: 200}
 
-%fieldset
-  %legend
-    Builds chart for last month
+.prepend-top-default
+  %p.light
+    Builds for last month
     (#{date_from_to(Date.today - 30.days, Date.today)})
+  %canvas#monthChart{height: 200}
 
-  %canvas#monthChart.padded{width: 800, height: 300}
-
-%fieldset
-  %legend Builds chart for last year
-  %canvas#yearChart.padded{width: 800, height: 400}
+.prepend-top-default
+  %p.light
+    Builds for last year
+  %canvas#yearChart.padded{height: 250}
 
 - [:week, :month, :year].each do |scope|
   :javascript
@@ -22,20 +32,20 @@
       labels : #{@charts[scope].labels.to_json},
       datasets : [
         {
-          fillColor : "rgba(220,220,220,0.5)",
-          strokeColor : "rgba(220,220,220,1)",
-          pointColor : "rgba(220,220,220,1)",
+          fillColor : "#7f8fa4",
+          strokeColor : "#7f8fa4",
+          pointColor : "#7f8fa4",
           pointStrokeColor : "#EEE",
           data : #{@charts[scope].total.to_json}
         },
         {
-          fillColor : "#4A3",
-          strokeColor : "rgba(151,187,205,1)",
-          pointColor : "rgba(151,187,205,1)",
+          fillColor : "#44aa22",
+          strokeColor : "#44aa22",
+          pointColor : "#44aa22",
           pointStrokeColor : "#fff",
           data : #{@charts[scope].success.to_json}
         }
       ]
     }
     var ctx = $("##{scope}Chart").get(0).getContext("2d");
-    new Chart(ctx).Line(data,{"scaleOverlay": true});
+    new Chart(ctx).Line(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false});
diff --git a/app/views/projects/graphs/ci/_overall.haml b/app/views/projects/graphs/ci/_overall.haml
index 9550d71947195ba2525559360f2d0edf5fc8db3b..cf4285a2671dc0d505834e5294f0471f6c040533 100644
--- a/app/views/projects/graphs/ci/_overall.haml
+++ b/app/views/projects/graphs/ci/_overall.haml
@@ -1,22 +1,20 @@
 - ci_project = @project.gitlab_ci_project
-%fieldset
-  %legend Overall
-  %p
+%h4 Overall stats
+%ul
+  %li
     Total:
     %strong= pluralize ci_project.builds.count(:all), 'build'
-  %p
+  %li
     Successful:
     %strong= pluralize ci_project.builds.success.count(:all), 'build'
-  %p
+  %li
     Failed:
     %strong= pluralize ci_project.builds.failed.count(:all), 'build'
-
-  %p
+  %li
     Success ratio:
     %strong
       #{success_ratio(ci_project.builds.success, ci_project.builds.failed)}%
-
-  %p
+  %li
     Commits covered:
     %strong
       = ci_project.commits.count(:all)
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index 112be875b6b918ad9a635e9fc1faa0abd50594d5..fc465ab273bd96bb1d8a838af53755546f470b5d 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -1,9 +1,13 @@
 - page_title "Commits", "Graphs"
 = render "header_title"
-.tree-ref-holder
-  = render 'shared/ref_switcher', destination: 'graphs_commits'
 = render 'head'
 
+.gray-content-block.append-bottom-default
+  .tree-ref-holder
+    = render 'shared/ref_switcher', destination: 'graphs_commits'
+  %ul.breadcrumb.repo-breadcrumb
+    = commits_breadcrumbs
+
 %p.lead
   Commit statistics for
   %strong #{@ref}
@@ -45,26 +49,24 @@
         Commits per weekday
       %canvas#weekday-chart
 
-:coffeescript
-  responsiveChart = (selector, data) ->
-    options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false }
-
-    # get selector by context
-    ctx = selector.get(0).getContext("2d")
-    # pointing parent container to make chart.js inherit its width
-    container = $(selector).parent()
-
-    generateChart = ->
-      selector.attr('width', $(container).width())
-      new Chart(ctx).Bar(data, options)
-
-    # enabling auto-resizing
-    $(window).resize( generateChart )
-
-    generateChart()
+:javascript
+  var responsiveChart = function (selector, data) {
+    var options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false };
+    // get selector by context
+    var ctx = selector.get(0).getContext("2d");
+    // pointing parent container to make chart.js inherit its width
+    var container = $(selector).parent();
+    var generateChart = function() {
+      selector.attr('width', $(container).width());
+      return new Chart(ctx).Bar(data, options);
+    };
+    // enabling auto-resizing
+    $(window).resize(generateChart);
+    return generateChart();
+  };
 
-  chartData = (keys, values) ->
-    data = {
+  var chartData = function (keys, values) {
+    var data = {
       labels : keys,
       datasets : [{
         fillColor : "rgba(220,220,220,0.5)",
@@ -74,13 +76,15 @@
         barDatasetSpacing: 1,
         data : values
       }]
-  }
+    };
+    return data;
+  };
 
-  hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json})
-  responsiveChart($('#hour-chart'), hourData)
+  var hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json});
+  responsiveChart($('#hour-chart'), hourData);
 
-  dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json})
-  responsiveChart($('#weekday-chart'), dayData)
+  var dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json});
+  responsiveChart($('#weekday-chart'), dayData);
 
-  monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json})
-  responsiveChart($('#month-chart'), monthData)
+  var monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json});
+  responsiveChart($('#month-chart'), monthData);
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index bd342911e49d04c056a6dc0a0f1ebc4931e393f3..882e7d6b6ee42c320cd529563af8a845d15215e4 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,9 +1,13 @@
 - page_title "Contributors", "Graphs"
 = render "header_title"
-.tree-ref-holder
-  = render 'shared/ref_switcher', destination: 'graphs'
 = render 'head'
 
+.gray-content-block.append-bottom-default
+  .tree-ref-holder
+    = render 'shared/ref_switcher', destination: 'graphs'
+  %ul.breadcrumb.repo-breadcrumb
+    = commits_breadcrumbs
+
 .loading-graph
   .center
     %h3.page-title
@@ -24,18 +28,21 @@
 
 
 
-:coffeescript
-  $.ajax
+:javascript
+  $.ajax({
     type: "GET",
     url: location.href,
-    success: (data) ->
-      graph = new ContributorsStatGraph()
-      graph.init(data)
+    dataType: "json",
+    success: function (data) {
+      var graph = new ContributorsStatGraph();
+      graph.init(data);
 
-      $("#brush_change").change ->
-        graph.change_date_header()
-        graph.redraw_authors()
+      $("#brush_change").change(function(){
+        graph.change_date_header();
+        graph.redraw_authors();
+      });
 
       $(".stat-graph").fadeIn();
       $(".loading-graph").hide();
-    dataType: "json"
+    }
+  });
diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml
index 85dbfd6786258ffc7dbf9b4daa554cccc5a1abbc..3702aeaecba0c396c8ab43d4968cc9cf6e249d95 100644
--- a/app/views/projects/hooks/index.html.haml
+++ b/app/views/projects/hooks/index.html.haml
@@ -19,7 +19,7 @@
       = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json'
   .form-group
     = f.label :url, "Trigger", class: 'control-label'
-    .col-sm-10
+    .col-sm-10.prepend-top-10
       %div
         = f.check_box :push_events, class: 'pull-left'
         .prepend-left-20
diff --git a/app/views/projects/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/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/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/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/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..30992412184e6a4cea4b00d86122c3912fd5cfd2 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 "http://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 "http://www.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/gitlab.yml.example b/config/gitlab.yml.example
index e297f393e3d9d046ba17671d50b54d0237efdfbe..20894ebcdc985ad68b402832a7fd721a5469ae2d 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -274,27 +274,28 @@ production: &base
     # arguments, followed by optional 'args' which can be either a hash or an array.
     # Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html
     providers:
-      # - { name: 'google_oauth2',
-      #     label: 'Google',
-      #     app_id: 'YOUR_APP_ID',
-      #     app_secret: 'YOUR_APP_SECRET',
-      #     args: { access_type: 'offline', approval_prompt: '' } }
-      # - { name: 'twitter',
-      #     app_id: 'YOUR_APP_ID',
-      #     app_secret: 'YOUR_APP_SECRET' }
       # - { name: 'github',
-      #     label: 'GitHub',
       #     app_id: 'YOUR_APP_ID',
       #     app_secret: 'YOUR_APP_SECRET',
       #     args: { scope: 'user:email' } }
+      # - { name: 'bitbucket',
+      #     app_id: 'YOUR_APP_ID',
+      #     app_secret: 'YOUR_APP_SECRET' }
       # - { name: 'gitlab',
-      #     label: 'GitLab.com',
       #     app_id: 'YOUR_APP_ID',
       #     app_secret: 'YOUR_APP_SECRET',
       #     args: { scope: 'api' } }
-      # - { name: 'bitbucket',
+      # - { name: 'google_oauth2',
+      #     app_id: 'YOUR_APP_ID',
+      #     app_secret: 'YOUR_APP_SECRET',
+      #     args: { access_type: 'offline', approval_prompt: '' } }
+      # - { name: 'facebook',
       #     app_id: 'YOUR_APP_ID',
       #     app_secret: 'YOUR_APP_SECRET' }
+      # - { name: 'twitter',
+      #     app_id: 'YOUR_APP_ID',
+      #     app_secret: 'YOUR_APP_SECRET' }
+      #
       # - { name: 'saml',
       #     label: 'Our SAML Provider',
       #     args: {
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index c189c88bdcba67fbab94444fff0060cffbb481bd..302124bd9771f1afe8754aa98e4ab15e1756f57b 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -181,10 +181,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,8 +194,8 @@ 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?
 
 #
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/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..c892c034f0102b24b1972ac49be0a942c7f1f762 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
@@ -472,8 +485,9 @@ Gitlab::Application.routes.draw do
         resources :commit, only: [:show], constraints: { id: /[[:alnum:]]{6,40}/ } do
           member do
             get :branches
-            get :ci
-            get :cancel_builds
+            get :builds
+            post :cancel_builds
+            post :retry_builds
           end
         end
 
@@ -569,7 +583,10 @@ Gitlab::Application.routes.draw do
         end
 
         resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
-        resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
+        resources :tags, only: [:index, :show, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do
+          resource :release, only: [:edit, :update]
+        end
+
         resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
         resource :variables, only: [:show, :update]
         resources :triggers, only: [:index, :create, :destroy]
@@ -588,12 +605,13 @@ Gitlab::Application.routes.draw do
 
         resources :builds, only: [:index, :show] do
           collection do
-            get :cancel_all
+            post :cancel_all
           end
 
           member do
-            get :cancel
             get :status
+            post :cancel
+            get :download
             post :retry
           end
         end
diff --git a/db/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/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/schema.rb b/db/schema.rb
index 4bde9f0b7482d13c251bbabd24f46f8b9286e2ce..f631d73f334ef2a6df33c3ff28087608704c73cc 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: 20151109100728) 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
@@ -501,14 +504,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 +641,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 +682,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..8e4a0ee1b82b4e2e53963c74001808bd4ca549a4 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -22,7 +22,8 @@ Parameters:
     "author_name": "Dmitriy Zaporozhets",
     "author_email": "dzaporozhets@sphereconsultinginc.com",
     "created_at": "2012-09-20T11:50:22+03:00",
-    "message": "Replace sanitize with escape once"
+    "message": "Replace sanitize with escape once",
+    "allow_failure": false
   },
   {
     "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
@@ -31,7 +32,8 @@ Parameters:
     "author_name": "randx",
     "author_email": "dmitriy.zaporozhets@gmail.com",
     "created_at": "2012-09-20T09:06:12+03:00",
-    "message": "Sanitize for network graph"
+    "message": "Sanitize for network graph",
+    "allow_failure": false
   }
 ]
 ```
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index c8122fc63b30016aecad548cfa6bb5f441f68900..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 applications to Heroku](test-and-deploy-ruby-application-to-heroku.md)
-+ [Test and deploy Python applications to Heroku](test-and-deploy-python-application-to-heroku.md)
-+ [Test Clojure applications](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/yaml/README.md b/doc/ci/yaml/README.md
index ea8f72bc1358e38831d50c6796e63c3c58391fee..5d35d1da4ee9496952d8bf19e0dbe193f1f00fb3 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -141,6 +141,7 @@ 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 |
 
 ### script
 `script` is a shell script which is executed by runner. The shell script is prepended with `before_script`.
@@ -169,7 +170,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 +183,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 +259,35 @@ 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.
+
 ## 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/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/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/install/installation.md b/doc/install/installation.md
index 599d2541f2cbbb3aa0db27ecaf0e12a7d2bce1c5..8028e51dbcdea99e929268b40b8b33b7eb5cbd5e 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -246,6 +246,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 +256,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
@@ -332,7 +335,7 @@ GitLab Shell is an SSH access and repository management software developed speci
     
     # Go to Gitlab installation folder
 
-    cd /home/git/gilab
+    cd /home/git/gitlab
 
     sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
 
diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md
new file mode 100644
index 0000000000000000000000000000000000000000..bc1f1673086d30f5475375a52990614bd9a48b90
--- /dev/null
+++ b/doc/integration/facebook.md
@@ -0,0 +1,97 @@
+# Facebook OAuth2 OmniAuth Provider
+
+To enable the Facebook OmniAuth provider you must register your application with Facebook. Facebook will generate an app ID and secret key for you to use.
+
+1.  Sign in to the [Facebook Developer Platform](https://developers.facebook.com/).
+
+1. Choose "My Apps" &gt; "Add a New App"
+
+1. Select the type "Website"
+
+1. Enter a name for your app. This can be anything. Consider something like "&lt;Organization&gt;'s GitLab" or "&lt;Your Name&gt;'s GitLab" or
+something else descriptive.
+
+1. Choose "Create New Facebook App ID"
+
+1. Select a Category, for example "Productivity"
+
+1. Choose "Create App ID"
+
+1. Enter the address of your GitLab installation at the bottom of the package
+
+    ![Facebook Website URL](facebook_website_url.png)
+
+1. Choose "Next"
+
+1. Choose "Skip Quick Start" in the upper right corner
+
+1. Choose "Settings" in the menu on the left
+
+1. Fill in a contact email for your app
+
+    ![Facebook App Settings](facebook_app_settings.png)
+
+1. Choose "Save Changes"
+
+1. Choose "Status & Review" in the menu on the left
+
+1. Change the switch on the right from No to Yes
+
+1. Choose "Confirm" when prompted to make the app public
+
+1. Choose "Dashboard" in the menu on the left
+
+1. Choose "Show" next to the hidden "App Secret"
+
+1. You should now see an app key and app secret (see screenshot). Keep this page open as you continue configuration.
+
+    ![Facebook API Keys](facebook_api_keys.png)
+
+1.  On your GitLab server, open the configuration file.
+
+    For omnibus package:
+
+    ```sh
+    sudo editor /etc/gitlab/gitlab.rb
+    ```
+
+    For installations from source:
+
+    ```sh
+    cd /home/git/gitlab
+
+    sudo -u git -H editor config/gitlab.yml
+    ```
+
+1.  See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings.
+
+1.  Add the provider configuration:
+
+    For omnibus package:
+
+    ```ruby
+    gitlab_rails['omniauth_providers'] = [
+      {
+        "name" => "facebook",
+        "app_id" => "YOUR_APP_ID",
+        "app_secret" => "YOUR_APP_SECRET"
+      }
+    ]
+    ```
+
+    For installations from source:
+
+    ```
+    - { name: 'facebook', app_id: 'YOUR_APP_ID',
+      app_secret: 'YOUR_APP_SECRET' }
+    ```
+
+1.  Change 'YOUR_APP_ID' to the API key from Facebook page in step 10.
+
+1.  Change 'YOUR_APP_SECRET' to the API secret from the Facebook page in step 10.
+
+1.  Save the configuration file.
+
+1.  Restart GitLab for the changes to take effect.
+
+On the sign in page there should now be a Facebook icon below the regular sign in form. Click the icon to begin the authentication process. Facebook will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in.
diff --git a/doc/integration/facebook_api_keys.png b/doc/integration/facebook_api_keys.png
new file mode 100644
index 0000000000000000000000000000000000000000..d6c44ac0f117feebe69adb17abc31312688a76eb
Binary files /dev/null and b/doc/integration/facebook_api_keys.png differ
diff --git a/doc/integration/facebook_app_settings.png b/doc/integration/facebook_app_settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..30dd21e198ad480931f4236ddb5f0b014efb12b4
Binary files /dev/null and b/doc/integration/facebook_app_settings.png differ
diff --git a/doc/integration/facebook_website_url.png b/doc/integration/facebook_website_url.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc3088bb2faa68b6555f83faee04b04ecc762bf7
Binary files /dev/null and b/doc/integration/facebook_website_url.png differ
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index c5cecbc2f2dc50e138ad7d15dab0b1b82f93b39d..bd9550c6ddb12cdefc688f9471763d4faf0862ce 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -73,8 +73,9 @@ Now we can choose one or more of the Supported Providers below to continue confi
 - [Bitbucket](bitbucket.md)
 - [GitLab.com](gitlab.md)
 - [Google](google.md)
-- [Shibboleth](shibboleth.md)
+- [Facebook](facebook.md)
 - [Twitter](twitter.md)
+- [Shibboleth](shibboleth.md)
 - [SAML](saml.md)
 - [Crowd](crowd.md)
 
diff --git a/doc/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..4925816daaa7be296aca4ab001893b0802a47711 100644
--- a/doc/release/monthly.md
+++ b/doc/release/monthly.md
@@ -25,68 +25,84 @@ If the release is falling behind immediately warn the team.
 
 ## Create an overall issue and follow it
 
-Create issue for GitLab CE project(internal). Name it "Release x.x.x" for easier searching.
-Replace the dates with actual dates based on the number of workdays before the release.
-All steps from issue template are explained below
+Create an issue in the GitLab CE project. Name it "Release x.x" and tag it with
+the `release` label for easier searching. Replace the dates with actual dates
+based on the number of workdays before the release. All steps from issue
+template are explained below:
 
 ```
-Xth: (7 working days before the 22nd)
+### Xth: (7 working days before the 22nd)
 
-- [ ] Triage the omnibus-gitlab milestone
+- [ ] Triage the [Omnibus milestone]
 
-Xth: (6 working days before the 22nd)
+### Xth: (6 working days before the 22nd)
 
-- [ ] Merge CE master in to EE master via merge request (#LINK)
+- [ ] Merge CE `master` into EE `master` via merge request (#LINK)
 - [ ] Determine QA person and notify this person
 - [ ] Check the tasks in [how to rc1 guide](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/release/howto_rc1.md) and delegate tasks if necessary
-- [ ] Create CE, EE, CI RC1 versions (#LINK)
-- [ ] Build RC1 packages (EE first) (#LINK)
+- [ ] Create CE and EE RC1 versions (#LINK)
+- [ ] Build RC1 packages
 
-Xth: (5 working days before the 22nd)
+### Xth: (5 working days before the 22nd)
 
 - [ ] Do QA and fix anything coming out of it (#LINK)
-- [ ] Close the omnibus-gitlab milestone
-- [ ] Prepare the blog post (#LINK)
+- [ ] Close the [Omnibus milestone]
+- [ ] Prepare the [blog post]
 
-Xth: (4 working days before the 22nd)
+### Xth: (4 working days before the 22nd)
 
-- [ ] Update GitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package)
-- [ ] Update ci.gitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package)
-- [ ] Create regression issues (CE, CI) (#LINK)
-- [ ] Tweet about rc1 (#LINK), proposed text: 
+- [ ] Update GitLab.com with RC1
+- [ ] Create the regression issue in the CE issue tracker:
 
-> GitLab x.x.0.rc1 is available https://packages.gitlab.com/gitlab/unstable Use at your own risk. Please link regressions issues from LINK_TO_REGRESSION_ISSUE
+    > This is a meta issue to index possible regressions in this monthly release
+    > and any patch versions.
+    >
+    > Please do not raise or discuss issues directly in this issue but link to
+    > issues that might warrant a patch release. If there is a Merge Request
+    > that fixes the issue, please link to that as well.
+    >
+    > Please only post one regression issue and/or merge request per comment.
+    > Comments will be updated by the release manager as they are addressed.
 
-Xth: (3 working days before the 22nd)
+- [ ] Tweet about RC1 release:
 
-- [ ] Merge CE stable branch into EE stable branch
+    > GitLab x.y.0.rc1 is available: https://packages.gitlab.com/gitlab/unstable
+    > Use at your own risk. Please link regressions issues from
+    > LINK_TO_REGRESSION_ISSUE
 
-Xth: (2 working days before the 22nd)
+### Xth: (3 working days before the 22nd)
 
-- [ ] Check that everyone is mentioned on the blog post using `@all` (the reviewer should have done this one working day ago)
-- [ ] Check that MVP is added to the mvp page (source/mvp/index.html in www-gitlab-com)
+- [ ] Merge `x-y-stable` into `x-y-stable-ee`
+- [ ] Check that everyone is mentioned on the [blog post] using `@all`
 
-Xth: (1 working day before the 22nd)
+### Xth: (2 working days before the 22nd)
 
-- [ ] Merge CE stable into EE stable
-- [ ] Create CE, EE, CI release candidates (#LINK) (hopefully final ones with the same commit as the release tomorrow) 
+- [ ] Check that MVP is added to the [MVP page]
+
+### Xth: (1 working day before the 22nd)
+
+- [ ] Merge `x-y-stable` into `x-y-stable-ee`
+- [ ] Create CE and EE release candidates
 - [ ] Create Omnibus tags and build packages for the latest release candidates
-- [ ] Update GitLab.com with the latest RC (#LINK)
-- [ ] Update ci.gitLab.com with the latest RC (#LINK)
+- [ ] Update GitLab.com with the latest RC
 
-22nd before 1200 CET:
+### 22nd before 1200 CET:
 
 Release before 1200 CET / 2AM PST, to make sure the majority of our users
 get the new version on the 22nd and there is sufficient time in the European
 workday to quickly fix any issues.
 
-- [ ] Merge CE stable into EE stable (#LINK)
-- [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools) (#LINK)
+- [ ] Merge `x-y-stable` into `x-y-stable-ee`
+- [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools)
 - [ ] Create the 'x.y.0' version on version.gitlab.com
-- [ ] Try to do before 1100 CET: Create and push omnibus tags for x.y.0 (will auto-release the packages) (#LINK)
-- [ ] Try to do before 1200 CET: Publish the release blog post (#LINK)
-- [ ] Tweet about the release (blog post) (#LINK)
-- [ ] Schedule a second tweet of the release announcement with the same text at 1800 CET / 8AM PST
+- [ ] Try to do before 1100 CET: Create and push Omnibus tags for x.y.0 (will auto-release the packages)
+- [ ] Try to do before 1200 CET: Publish the release [blog post]
+- [ ] Tweet about the release
+- [ ] Schedule a second Tweet of the release announcement with the same text at 1800 CET / 8AM PST
+
+[Omnibus milestone]: LINK_TO_OMNIBUS_MILESTONE
+[blog post]: LINK_TO_WIP_BLOG_POST
+[MVP page]: https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/source/mvp/index.html
 ```
 
 - - -
diff --git a/doc/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_styleguide.md b/doc_styleguide.md
index 656bb1d17ff244be0bf421deb537197bb37e8f17..cceb449a8543ac1155a61afaed5b0a0405645937 100644
--- a/doc_styleguide.md
+++ b/doc_styleguide.md
@@ -15,6 +15,8 @@ For subtitles, use '##', '###' and so on.
 - Do not duplicate information.
 - Be brief and clear.
 - Whenever it applies, add documents in alphabetical order.
+- Write in US English
+- Use [single spaces](http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html) instead of double spaces.
 
 ## Images
 
diff --git a/features/profile/profile.feature b/features/profile/profile.feature
index 27c0bde364e989f9267c27dbfd528e4abd556331..168d9d30b508d23017d4edc6682086870fdd9da1 100644
--- a/features/profile/profile.feature
+++ b/features/profile/profile.feature
@@ -7,6 +7,7 @@ Feature: Profile
     Given I visit profile page
     Then I should see my profile info
 
+  @javascript
   Scenario: I can see groups I belong to
     Given I have group with projects
     When I visit profile page
diff --git a/features/project/commits/tags.feature b/features/project/commits/tags.feature
index 02f399f7cad9038ee4ddc900c2d0261a21d4059c..56ee091acc0399cf07220a17251b965014e652bb 100644
--- a/features/project/commits/tags.feature
+++ b/features/project/commits/tags.feature
@@ -12,6 +12,12 @@ Feature: Project Commits Tags
     And I submit new tag form
     Then I should see new tag created
 
+  Scenario: I create a tag with release notes
+    Given I click new tag link
+    And I submit new tag form with release notes
+    Then I should see new tag created
+    And I should see tag release notes
+
   Scenario: I create a tag with invalid name
     And I click new tag link
     And I submit new tag form with invalid name
@@ -27,15 +33,13 @@ Feature: Project Commits Tags
     And I submit new tag form with tag that already exists
     Then I should see new an error that tag already exists
 
-  @javascript
   Scenario: I delete a tag
+    Given I visit tag 'v1.1.0' page
     Given I delete tag 'v1.1.0'
     Then I should not see tag 'v1.1.0'
 
-  @javascript
-  Scenario: I delete all tags and see info message
-    Given I delete all tags
-    Then I should see tags info message
-
-  # @wip
-  # Scenario: I can download project by tag
+  Scenario: I add release notes to the tag
+    Given I visit tag 'v1.1.0' page
+    When I click edit tag link
+    And I fill release notes and submit form
+    Then I should see tag release notes
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 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 69ddfa42c062df0d9f9b3d49b0c6bdd889d6ec46..70388c18fcf637e31861d7919a79e9bfdcfc5053 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -6,7 +6,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
   include Select2Helper
 
   step 'I should see back to dashboard button' do
-    expect(page).to have_content 'Back to dashboard'
+    expect(page).to have_content 'Go to dashboard'
   end
 
   step 'gitlab user "Mike"' do
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 8cf24705a5ecc15d5afc8b8a240ad4d11a99ed0a..40b2aa7c357761bde578679b678d1bf39a1cb6ae 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -59,7 +59,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
   step 'I should not see the "Remove avatar" button' do
     expect(page).not_to have_link("Remove avatar")
   end
-  
+
   step 'I should see the gravatar host link' do
     expect(page).to have_link("gravatar.com")
   end
@@ -159,10 +159,9 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
   end
 
   step 'I should see my user page' do
-    expect(page).to have_content "User Activity"
-
-    page.within '.navbar-gitlab' do
+    page.within ".cover-block" do
       expect(page).to have_content current_user.name
+      expect(page).to have_content current_user.username
     end
   end
 
@@ -176,7 +175,13 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
   end
 
   step 'I should see groups I belong to' do
-    expect(page).to have_css('.profile-groups-avatars', visible: true)
+    page.within ".content" do
+      click_link "Groups"
+    end
+
+    page.within "#groups" do
+      expect(page).to have_content @group.name
+    end
   end
 
   step 'I click on new application button' do
diff --git a/features/steps/project/commits/tags.rb b/features/steps/project/commits/tags.rb
index e6f8faf50fdec75a764adaca1e26317ba9e8cec4..eff4234a44a2487dfd8c718508eca529dfb6bbbc 100644
--- a/features/steps/project/commits/tags.rb
+++ b/features/steps/project/commits/tags.rb
@@ -18,6 +18,18 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps
     click_button 'Create tag'
   end
 
+  step 'I submit new tag form with release notes' do
+    fill_in 'tag_name', with: 'v7.0'
+    fill_in 'ref', with: 'master'
+    fill_in 'release_description', with: 'Awesome release notes'
+    click_button 'Create tag'
+  end
+
+  step 'I fill release notes and submit form' do
+    fill_in 'release_description', with: 'Awesome release notes'
+    click_button 'Save changes'
+  end
+
   step 'I submit new tag form with invalid name' do
     fill_in 'tag_name', with: 'v 1.0'
     fill_in 'ref', with: 'master'
@@ -52,31 +64,27 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps
     expect(page).to have_content 'Tag already exists'
   end
 
+  step "I visit tag 'v1.1.0' page" do
+    click_link 'v1.1.0'
+  end
+
   step "I delete tag 'v1.1.0'" do
-    page.within '.tags' do
+    page.within('.content') do
       first('.btn-remove').click
-      sleep 0.05
     end
   end
 
   step "I should not see tag 'v1.1.0'" do
     page.within '.tags' do
-      expect(page.all(visible: true)).not_to have_content 'v1.1.0'
+      expect(page).not_to have_link 'v1.1.0'
     end
   end
 
-  step 'I delete all tags' do
-    page.within '.tags' do
-      page.all('.btn-remove').each do |remove|
-        remove.click
-        sleep 0.05
-      end
-    end
+  step 'I click edit tag link' do
+    click_link 'Edit release notes'
   end
 
-  step 'I should see tags info message' do
-    page.within '.tags' do
-      expect(page).to have_content 'Repository has no tags yet.'
-    end
+  step 'I should see tag release notes' do
+    expect(page).to have_content 'Awesome release notes'
   end
 end
diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb
index 4abd5288d51de108d9467bafed76fa1ddb2de36f..98f31f3b76a194d854087e70264c45910dc5f24f 100644
--- a/features/steps/project/graph.rb
+++ b/features/steps/project/graph.rb
@@ -25,9 +25,9 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
 
   step 'page should have CI graphs' do
     expect(page).to have_content 'Overall'
-    expect(page).to have_content 'Builds chart for last week'
-    expect(page).to have_content 'Builds chart for last month'
-    expect(page).to have_content 'Builds chart for last year'
+    expect(page).to have_content 'Builds for last week'
+    expect(page).to have_content 'Builds for last month'
+    expect(page).to have_content 'Builds for last year'
     expect(page).to have_content 'Commit duration in minutes for last 30 commits'
   end
 
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index 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/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/entities.rb b/lib/api/entities.rb
index 883a5e14b17cea75fc45ed50d82b9fadf7533c19..20cadae2291f7ebb52fbc6818fd6de7ba5999290 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -231,7 +231,7 @@ module API
 
     class CommitStatus < Grape::Entity
       expose :id, :sha, :ref, :status, :name, :target_url, :description,
-             :created_at, :started_at, :finished_at
+             :created_at, :started_at, :finished_at, :allow_failure
       expose :author, using: Entities::UserBasic
     end
 
diff --git a/lib/api/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/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 800f30c21449b129640d04beae29b3b336ce7bce..635967f4bd465949847382d6e0b030b07c9144e5 100644
--- a/lib/backup/builds.rb
+++ b/lib/backup/builds.rb
@@ -1,3 +1,5 @@
+require 'backup/files'
+
 module Backup
   class Builds < Files
     def initialize
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index f011fd03de0b06c001d6b75b85d2ca1ef0bff226..9e15d5411a1742760434937936ddd2fa1621c99b 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -150,7 +150,7 @@ module Backup
     private
 
     def backup_contents
-      folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "backup_information.yml"]
+      folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "artifacts.tar.gz", "backup_information.yml"]
     end
 
     def folders_to_backup
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 0a0ec564ba4f63db3ac6561cdc2def184fcb062c..9261f77f3c9d45ee137dd450ba5a6c401267c592 100644
--- a/lib/backup/uploads.rb
+++ b/lib/backup/uploads.rb
@@ -1,3 +1,5 @@
+require 'backup/files'
+
 module Backup
   class Uploads < Files
 
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..2e2209031eef28bf7e0d4bf40d1eb0109b4b1c6a 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -5,12 +5,13 @@ module Ci
     DEFAULT_STAGES = %w(build test deploy)
     DEFAULT_STAGE = 'test'
     ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables]
-    ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when]
+    ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts]
 
-    attr_reader :before_script, :image, :services, :variables
+    attr_reader :before_script, :image, :services, :variables, :path
 
-    def initialize(config)
+    def initialize(config, path = nil)
       @config = YAML.load(config)
+      @path = path
 
       unless @config.is_a? Hash
         raise ValidationError, "YAML should be a hash"
@@ -63,26 +64,6 @@ module Ci
       end
     end
 
-    def process?(only_params, except_params, ref, tag)
-      return true if only_params.nil? && except_params.nil?
-
-      if only_params
-        return true if tag && only_params.include?("tags")
-        return true if !tag && only_params.include?("branches")
-        
-        only_params.find do |pattern|
-          match_ref?(pattern, ref)
-        end
-      else
-        return false if tag && except_params.include?("tags")
-        return false if !tag && except_params.include?("branches")
-
-        except_params.each do |pattern|
-          return false if match_ref?(pattern, ref)
-        end
-      end
-    end
-
     def build_job(name, job)
       {
         stage_idx: stages.index(job[:stage]),
@@ -96,19 +77,12 @@ module Ci
         when: job[:when] || 'on_success',
         options: {
           image: job[:image] || @image,
-          services: job[:services] || @services
+          services: job[:services] || @services,
+          artifacts: job[:artifacts]
         }.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")
@@ -186,7 +160,17 @@ 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[: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 +192,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/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_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/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/nginx/gitlab b/lib/support/nginx/gitlab
index e767027dc29ad33c3ae1609e8201298c5f8b2b0f..0a7a4118077bb271ae3bf700c017c0e390380dbf 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -131,6 +131,22 @@ server {
     return 418;
   }
 
+  # 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.
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 4d31e31f8d57a9f752de0d7d22a46a04f711113f..b463d5b6aa991b2a45766b25be853f6d65749e9e 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -178,6 +178,22 @@ server {
     return 418;
   }
 
+  # 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.
diff --git a/lib/tasks/flay.rake b/lib/tasks/flay.rake
new file mode 100644
index 0000000000000000000000000000000000000000..5efffc2cdaccf13598c347a955e7ac4c84f1c371
--- /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 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/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 4cdba66939ba82f6b93c962a966938d0029f114b..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|
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/factories/ci/projects.rb b/spec/factories/ci/projects.rb
index 111e1a8281680219c26870507ab8e3d35e687293..1183a190353e7f97e827c0709278712e5e625e89 100644
--- a/spec/factories/ci/projects.rb
+++ b/spec/factories/ci/projects.rb
@@ -33,6 +33,8 @@ FactoryGirl.define do
 
     gl_project factory: :empty_project
 
+    shared_runners_enabled false
+
     factory :ci_project do
       token 'iPWx6WM4lhHNedGfBpPJNP'
     end
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
new file mode 100644
index 0000000000000000000000000000000000000000..80d6bbee6c75ad952decf928fc574ff913d8815f
--- /dev/null
+++ b/spec/factories/releases.rb
@@ -0,0 +1,9 @@
+# Read about factories at https://github.com/thoughtbot/factory_girl
+
+FactoryGirl.define do
+  factory :release do
+    tag "v1.1.0"
+    description "Awesome release"
+    project
+  end
+end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 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/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..29fc271382113d8138b1a1575b3c7f65c91442a9 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"] }
+                             })
 
-        expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
+          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"] },
+                             })
+
+          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_processor = GitlabCiYamlProcessor.new(config, path)
 
-        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"] },
-                           })
+          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
+        end
 
-        config_processor = GitlabCiYamlProcessor.new(config)
+        it "returns builds if except has regexp with another branch" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", except: ["/^deploy$/"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
+        end
+
+        it "does not return builds if except has specified this branch" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", except: ["master"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
+
+          expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
+        end
+
+        it "does not return builds if except has a list of branches including specified" do
+          config = YAML.dump({
+                               before_script: ["pwd"],
+                               rspec: { script: "rspec", type: type, except: ["master", "deploy"] }
+                             })
+
+          config_processor = GitlabCiYamlProcessor.new(config, path)
 
-        expect(config_processor.builds_for_stage_and_ref("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, "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,43 @@ module Ci
       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 +378,170 @@ 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
     end
   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..f01fe8bd3982e9c0418f74725a3be569b6edb2b6 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -28,11 +28,11 @@
 require 'spec_helper'
 
 describe ApplicationSetting, models: true do
-  it { expect(ApplicationSetting.create_from_defaults).to be_valid }
+  let(:setting) { ApplicationSetting.create_from_defaults }
 
-  context 'restricted signup domains' do
-    let(:setting) { ApplicationSetting.create_from_defaults }
+  it { expect(setting).to be_valid }
 
+  context 'restricted signup domains' do
     it 'set single domain' do
       setting.restricted_signup_domains_raw = 'example.com'
       expect(setting.restricted_signup_domains).to eq(['example.com'])
@@ -53,4 +53,26 @@ describe ApplicationSetting, models: true do
       expect(setting.restricted_signup_domains).to eq(['example.com', '*.example.com'])
     end
   end
+
+  context 'shared runners' do
+    let(:gl_project) { create(:empty_project) }
+
+    before do
+      allow_any_instance_of(Project).to receive(:current_application_settings).and_return(setting)
+    end
+
+    subject { gl_project.ensure_gitlab_ci_project.shared_runners_enabled }
+
+    context 'enabled' do
+      before { setting.update_attributes(shared_runners_enabled: true) }
+
+      it { is_expected.to be_truthy }
+    end
+
+    context 'disabled' do
+      before { setting.update_attributes(shared_runners_enabled: false) }
+
+      it { is_expected.to be_falsey }
+    end
+  end
 end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 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/group_spec.rb b/spec/models/group_spec.rb
index 80638fc8db2216fff6c064ea96f83dad5186ca64..0f23e81ace9f3f1d8e23f3d6524c989b194ed68f 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -84,4 +84,23 @@ describe Group do
       expect(group.avatar_type).to eq(["only images allowed"])
     end
   end
+
+  describe "public_profile?" do
+    it "returns true for public group" do
+      group = create(:group, public: true)
+      expect(group.public_profile?).to be_truthy
+    end
+
+    it "returns true for non-public group with public project" do
+      group = create(:group)
+      create(:project, :public, group: group)
+      expect(group.public_profile?).to be_truthy
+    end
+
+    it "returns false for non-public group with no public projects" do
+      group = create(:group)
+      create(:project, group: group)
+      expect(group.public_profile?).to be_falsy
+    end
+  end
 end
diff --git a/spec/models/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_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 94802dcfb79f7486c7ca36ce4c618bac9aca611e..9f6cdeeaa962b1e0b8245d043628f4a65f085b18 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -223,7 +223,7 @@ describe ProjectWiki do
 
   def create_temp_repo(path)
     FileUtils.mkdir_p path
-    system(*%W(git init --quiet --bare -- #{path}))
+    system(*%W(#{Gitlab.config.git.bin_path} init --quiet --bare -- #{path}))
   end
 
   def remove_temp_repo(path)
diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..527005b2b698adae9c53e09ae84bcd81b6ebc18a
--- /dev/null
+++ b/spec/models/release_spec.rb
@@ -0,0 +1,16 @@
+require 'rails_helper'
+
+RSpec.describe Release, type: :model do
+  let(:release) { create(:release) }
+
+  it { expect(release).to be_valid }
+
+  describe 'associations' do
+    it { is_expected.to belong_to(:project) }
+  end
+
+  describe 'validation' do
+    it { is_expected.to validate_presence_of(:project) }
+    it { is_expected.to validate_presence_of(:description) }
+  end
+end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 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..49e0bfdd2ec91c8be9ab5f47b2e703e8406ebe0a 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -663,24 +663,24 @@ describe User do
       @user1 = create :user, created_at: Date.today - 1, last_sign_in_at: Date.today - 1, name: 'Omega'
     end
 
-    it "sorts users as recently_signed_in" do
+    it "sorts users by the recent sign-in time" do
       expect(User.sort('recent_sign_in').first).to eq(@user)
     end
 
-    it "sorts users as late_signed_in" do
+    it "sorts users by the oldest sign-in time" do
       expect(User.sort('oldest_sign_in').first).to eq(@user1)
     end
 
-    it "sorts users as recently_created" do
+    it "sorts users in descending order by their creation time" do
       expect(User.sort('created_desc').first).to eq(@user)
     end
 
-    it "sorts users as late_created" do
+    it "sorts users in ascending order by their creation time" do
       expect(User.sort('created_asc').first).to eq(@user1)
     end
 
-    it "sorts users by name when nil is passed" do
-      expect(User.sort(nil).first).to eq(@user)
+    it "sorts users by id in descending order when nil is passed" do
+      expect(User.sort(nil).first).to eq(@user1)
     end
   end
 
diff --git a/spec/requests/api/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/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 1149f7e79895d80c58cf594302d39b5e6a5bd844..faf6b77a4629d20ea8dcb1bf84588f35bf4e8af2 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -36,8 +36,8 @@ describe API::API, api: true  do
       it 'should create a new annotated tag' do
         # Identity must be set in .gitconfig to create annotated tag.
         repo_path = project.repository.path_to_repo
-        system(*%W(git --git-dir=#{repo_path} config user.name #{user.name}))
-        system(*%W(git --git-dir=#{repo_path} config user.email #{user.email}))
+        system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name}))
+        system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email}))
 
         post api("/projects/#{project.id}/repository/tags", user),
              tag_name: 'v7.1.0',
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..7886a6feca2480b4fd768af892384f4da0ee4a08 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -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,194 @@ 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
+            settings = Gitlab::CurrentSettings::current_application_settings
+            settings.update_attributes(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
+            settings = Gitlab::CurrentSettings::current_application_settings
+            settings.update_attributes(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
+              settings = Gitlab::CurrentSettings::current_application_settings
+              settings.update_attributes(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/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/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 386ac9c8372007aec9104028445c94a4c016854d..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
@@ -56,6 +56,7 @@ describe 'gitlab:app namespace rake task' do
         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
@@ -113,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.tar.gz repositories builds.tar.gz}
+        %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.tar.gz')
       expect(tar_contents).to match('repositories/')
       expect(tar_contents).to match('builds.tar.gz')
-      expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz)\/$/)
+      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
@@ -161,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.tar.gz repositories builds.tar.gz}
+        %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.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
 
@@ -178,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