diff --git a/.rubocop.yml b/.rubocop.yml
index 71273ce6098f7e2f2aa88b470b91b4edd32882be..2fda0b03119adccf18fe46b3186db1d978e930af 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -691,7 +691,7 @@ Style/ZeroLengthPredicate:
 # branches, and conditions.
 Metrics/AbcSize:
   Enabled: true
-  Max: 70
+  Max: 60
 
 # Avoid excessive block nesting.
 Metrics/BlockNesting:
diff --git a/CHANGELOG b/CHANGELOG
index 151875249d8c459f607d0da77c435c62d5a20cbd..3561c541df04dddb459e7ae7a91400826b1d724b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,8 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
 v 8.7.0 (unreleased)
+  - Enable gzip for assets, makes the page size significantly smaller. !3544 / !3632 (Connor Shea)
+  - Load award emoji images separately unless opening the full picker. Saves several hundred KBs of data for most pages. (Connor Shea)
   - All images in discussions and wikis now link to their source files !3464 (Connor Shea).
   - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu)
   - Improved Markdown rendering performance !3389 (Yorick Peterse)
@@ -12,6 +14,7 @@ v 8.7.0 (unreleased)
   - Allow back dating on issues when created through the API
   - Fix Error 500 after renaming a project path (Stan Hu)
   - Fix avatar stretching by providing a cropping feature
+  - API: Expose `subscribed` for issues and merge requests (Robert Schilling)
   - Allow SAML to handle external users based on user's information !3530
   - Add endpoints to archive or unarchive a project !3372
   - Add links to CI setup documentation from project settings and builds pages
@@ -26,19 +29,24 @@ v 8.7.0 (unreleased)
   - Fix creation of merge requests for orphaned branches (Stan Hu)
   - Fall back to `In-Reply-To` and `References` headers when sub-addressing is not available (David Padilla)
   - Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
-  - Improved UX of the navigation sidebar
   - Fix admin/projects when using visibility levels on search (PotHix)
   - Build status notifications
   - API: Expose user location (Robert Schilling)
   - ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
   - Update number of Todos in the sidebar when it's marked as "Done". !3600
 
-v 8.6.5 (unreleased)
-  - Only update repository language if it is not set to improve performance
-  - Check permissions when user attempts to import members from another project
+v 8.6.5
+  - Fix importing from GitHub Enterprise. !3529
+  - Perform the language detection after updating merge requests in `GitPushService`, leading to faster visual feedback for the end-user. !3533
+  - Check permissions when user attempts to import members from another project. !3535
+  - Only update repository language if it is not set to improve performance. !3556
+  - Return status code 303 after a branch DELETE operation to avoid project deletion (Stan Hu). !3583
+  - Unblock user when active_directory is disabled and it can be found !3550
+  - Fix a 2FA authentication spoofing vulnerability.
 
 v 8.6.4
   - Don't attempt to fetch any tags from a forked repo (Stan Hu)
+  - Redesign the Labels page
 
 v 8.6.3
   - Mentions on confidential issues doesn't create todos for non-members. !3374
@@ -155,6 +163,9 @@ v 8.6.0
   - Trigger a todo for mentions on commits page
   - Let project owners and admins soft delete issues and merge requests
 
+v 8.5.10
+  - Fix a 2FA authentication spoofing vulnerability.
+
 v 8.5.9
   - Don't attempt to fetch any tags from a forked repo (Stan Hu).
 
@@ -299,6 +310,9 @@ v 8.5.0
   - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
   - Add Todos
 
+v 8.4.8
+  - Fix a 2FA authentication spoofing vulnerability.
+
 v 8.4.7
   - Don't attempt to fetch any tags from a forked repo (Stan Hu).
 
@@ -418,6 +432,9 @@ v 8.4.0
   - Add IP check against DNSBLs at account sign-up
   - Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
 
+v 8.3.7
+  - Fix a 2FA authentication spoofing vulnerability.
+
 v 8.3.6
   - Don't attempt to fetch any tags from a forked repo (Stan Hu).
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 511336f384ce7ca415bbf3543f28757780985233..1f26a5d7eaf434a6aa1bc9042a1aabc923798197 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -448,7 +448,7 @@ merge request:
     - multi-line method chaining style **Option B**: dot `.` on previous line
     - string literal quoting style **Option A**: single quoted by default
 1.  [Rails](https://github.com/bbatsov/rails-style-guide)
-1.  [Testing](https://github.com/thoughtbot/guides/tree/master/style/testing)
+1.  [Testing](doc/development/testing.md)
 1.  [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript)
 1.  [SCSS styleguide][scss-styleguide]
 1.  [Shell commands](doc/development/shell_commands.md) created by GitLab
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 24ba9a38de68d00674ec97b283a967699716b9f4..37c2961c2430f357166156e7ddf1c590eb8d4ce1 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-2.7.0
+2.7.2
diff --git a/Gemfile b/Gemfile
index 0279b4ac47e56a266cc8a5d16fa1ce7bb185b0e2..258b5612cd59e75cd3ea41cc0c4967a0f0ac6a56 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
 source "https://rubygems.org"
 
-gem 'rails', '4.2.5.2'
+gem 'rails', '4.2.6'
 gem 'rails-deprecated_sanitizer', '~> 1.0.3'
 
 # Responders respond_to and respond_with
@@ -8,7 +8,7 @@ gem 'responders', '~> 2.0'
 
 # Specify a sprockets version due to increased performance
 # See https://gitlab.com/gitlab-org/gitlab-ce/issues/6069
-gem 'sprockets', '~> 3.3.5'
+gem 'sprockets', '~> 3.6.0'
 
 # Default values for AR models
 gem "default_value_for", "~> 3.0.0"
@@ -149,6 +149,10 @@ gem 'version_sorter', '~> 2.0.0'
 # Cache
 gem "redis-rails", '~> 4.0.0'
 
+# Redis
+gem 'redis', '~> 3.2'
+gem 'connection_pool', '~> 2.0'
+
 # Campfire integration
 gem 'tinder', '~> 1.10.0'
 
@@ -229,14 +233,13 @@ group :metrics do
   gem 'allocations', '~> 1.0', require: false, platform: :mri
   gem 'method_source', '~> 0.8', require: false
   gem 'influxdb', '~> 0.2', require: false
-  gem 'connection_pool', '~> 2.0', require: false
 end
 
 group :development do
   gem "foreman"
   gem 'brakeman', '~> 3.2.0', require: false
 
-  gem "annotate", "~> 2.6.0"
+  gem "annotate", "~> 2.7.0"
   gem "letter_opener", '~> 1.1.2'
   gem 'quiet_assets', '~> 1.0.2'
   gem 'rerun', '~> 0.11.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 1ba8d748db14894a2e3e0111baf896def7304ea1..9da44a46583d6c6dc384ea0b8681ab48ad5791d8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -4,41 +4,41 @@ GEM
     CFPropertyList (2.3.2)
     RedCloth (4.2.9)
     ace-rails-ap (2.0.1)
-    actionmailer (4.2.5.2)
-      actionpack (= 4.2.5.2)
-      actionview (= 4.2.5.2)
-      activejob (= 4.2.5.2)
+    actionmailer (4.2.6)
+      actionpack (= 4.2.6)
+      actionview (= 4.2.6)
+      activejob (= 4.2.6)
       mail (~> 2.5, >= 2.5.4)
       rails-dom-testing (~> 1.0, >= 1.0.5)
-    actionpack (4.2.5.2)
-      actionview (= 4.2.5.2)
-      activesupport (= 4.2.5.2)
+    actionpack (4.2.6)
+      actionview (= 4.2.6)
+      activesupport (= 4.2.6)
       rack (~> 1.6)
       rack-test (~> 0.6.2)
       rails-dom-testing (~> 1.0, >= 1.0.5)
       rails-html-sanitizer (~> 1.0, >= 1.0.2)
-    actionview (4.2.5.2)
-      activesupport (= 4.2.5.2)
+    actionview (4.2.6)
+      activesupport (= 4.2.6)
       builder (~> 3.1)
       erubis (~> 2.7.0)
       rails-dom-testing (~> 1.0, >= 1.0.5)
       rails-html-sanitizer (~> 1.0, >= 1.0.2)
-    activejob (4.2.5.2)
-      activesupport (= 4.2.5.2)
+    activejob (4.2.6)
+      activesupport (= 4.2.6)
       globalid (>= 0.3.0)
-    activemodel (4.2.5.2)
-      activesupport (= 4.2.5.2)
+    activemodel (4.2.6)
+      activesupport (= 4.2.6)
       builder (~> 3.1)
-    activerecord (4.2.5.2)
-      activemodel (= 4.2.5.2)
-      activesupport (= 4.2.5.2)
+    activerecord (4.2.6)
+      activemodel (= 4.2.6)
+      activesupport (= 4.2.6)
       arel (~> 6.0)
     activerecord-deprecated_finders (1.0.4)
     activerecord-session_store (0.1.2)
       actionpack (>= 4.0.0, < 5)
       activerecord (>= 4.0.0, < 5)
       railties (>= 4.0.0, < 5)
-    activesupport (4.2.5.2)
+    activesupport (4.2.6)
       i18n (~> 0.7)
       json (~> 1.7, >= 1.7.7)
       minitest (~> 5.1)
@@ -51,8 +51,8 @@ GEM
       activerecord (>= 3.0)
     akismet (2.0.0)
     allocations (1.0.4)
-    annotate (2.6.10)
-      activerecord (>= 3.2, <= 4.3)
+    annotate (2.7.0)
+      activerecord (>= 3.2, < 6.0)
       rake (~> 10.4)
     arel (6.0.3)
     asana (0.4.0)
@@ -145,7 +145,7 @@ GEM
     crack (0.4.3)
       safe_yaml (~> 1.0.0)
     creole (0.5.0)
-    css_parser (1.3.7)
+    css_parser (1.4.1)
       addressable
     d3_rails (3.5.11)
       railties (>= 3.1.0)
@@ -459,8 +459,8 @@ GEM
       nokogiri (>= 1.5.9)
     macaddr (1.7.1)
       systemu (~> 2.6.2)
-    mail (2.6.3)
-      mime-types (>= 1.16, < 3)
+    mail (2.6.4)
+      mime-types (>= 1.16, < 4)
     mail_room (0.6.1)
     method_source (0.8.2)
     mime-types (1.25.1)
@@ -559,8 +559,8 @@ GEM
     premailer (1.8.6)
       css_parser (>= 1.3.6)
       htmlentities (>= 4.0.0)
-    premailer-rails (1.9.0)
-      actionmailer (>= 3, < 5)
+    premailer-rails (1.9.2)
+      actionmailer (>= 3, < 6)
       premailer (~> 1.7, >= 1.7.9)
     pry (0.10.3)
       coderay (~> 1.1.0)
@@ -589,16 +589,16 @@ GEM
       rack
     rack-test (0.6.3)
       rack (>= 1.0)
-    rails (4.2.5.2)
-      actionmailer (= 4.2.5.2)
-      actionpack (= 4.2.5.2)
-      actionview (= 4.2.5.2)
-      activejob (= 4.2.5.2)
-      activemodel (= 4.2.5.2)
-      activerecord (= 4.2.5.2)
-      activesupport (= 4.2.5.2)
+    rails (4.2.6)
+      actionmailer (= 4.2.6)
+      actionpack (= 4.2.6)
+      actionview (= 4.2.6)
+      activejob (= 4.2.6)
+      activemodel (= 4.2.6)
+      activerecord (= 4.2.6)
+      activesupport (= 4.2.6)
       bundler (>= 1.3.0, < 2.0)
-      railties (= 4.2.5.2)
+      railties (= 4.2.6)
       sprockets-rails
     rails-deprecated_sanitizer (1.0.3)
       activesupport (>= 4.2.0.alpha)
@@ -608,9 +608,9 @@ GEM
       rails-deprecated_sanitizer (>= 1.0.1)
     rails-html-sanitizer (1.0.3)
       loofah (~> 2.0)
-    railties (4.2.5.2)
-      actionpack (= 4.2.5.2)
-      activesupport (= 4.2.5.2)
+    railties (4.2.6)
+      actionpack (= 4.2.6)
+      activesupport (= 4.2.6)
       rake (>= 0.8.7)
       thor (>= 0.18.1, < 2.0)
     rainbow (2.1.0)
@@ -776,12 +776,13 @@ GEM
       spring (>= 0.9.1)
     spring-commands-teaspoon (0.0.2)
       spring (>= 0.9.1)
-    sprockets (3.3.5)
+    sprockets (3.6.0)
+      concurrent-ruby (~> 1.0)
       rack (> 1, < 3)
-    sprockets-rails (2.3.3)
-      actionpack (>= 3.0)
-      activesupport (>= 3.0)
-      sprockets (>= 2.8, < 4.0)
+    sprockets-rails (3.0.4)
+      actionpack (>= 4.0)
+      activesupport (>= 4.0)
+      sprockets (>= 3.0.0)
     state_machines (0.4.0)
     state_machines-activemodel (0.3.0)
       activemodel (~> 4.1)
@@ -887,7 +888,7 @@ DEPENDENCIES
   after_commit_queue
   akismet (~> 2.0)
   allocations (~> 1.0)
-  annotate (~> 2.6.0)
+  annotate (~> 2.7.0)
   asana (~> 0.4.0)
   asciidoctor (~> 1.5.2)
   attr_encrypted (~> 1.3.4)
@@ -992,13 +993,14 @@ DEPENDENCIES
   rack-attack (~> 4.3.1)
   rack-cors (~> 0.4.0)
   rack-oauth2 (~> 1.2.1)
-  rails (= 4.2.5.2)
+  rails (= 4.2.6)
   rails-deprecated_sanitizer (~> 1.0.3)
   raphael-rails (~> 2.1.2)
   rblineprof
   rdoc (~> 3.6)
   recaptcha
   redcarpet (~> 3.3.3)
+  redis (~> 3.2)
   redis-namespace
   redis-rails (~> 4.0.0)
   request_store (~> 1.3.0)
@@ -1032,7 +1034,7 @@ DEPENDENCIES
   spring-commands-rspec (~> 1.0.4)
   spring-commands-spinach (~> 1.0.0)
   spring-commands-teaspoon (~> 0.0.2)
-  sprockets (~> 3.3.5)
+  sprockets (~> 3.6.0)
   state_machines-activerecord (~> 0.3.0)
   task_list (~> 1.0.2)
   teaspoon (~> 1.1.0)
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index 6a670d5e887bd01d617c6bcb9c8e749184b71456..af4462ece385b089ea3b68ac7fbe9fb93ae9ed99 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -22,8 +22,19 @@ class @AwardsHandler
     emoji = $(this)
       .find(".icon")
       .data "emoji"
+
+    if emoji is "thumbsup" and awards_handler.didUserClickEmoji $(this), "thumbsdown"
+      awards_handler.addAward "thumbsdown"
+
+    else if emoji is "thumbsdown" and awards_handler.didUserClickEmoji $(this), "thumbsup"
+      awards_handler.addAward "thumbsup"
+
     awards_handler.addAward emoji
 
+  didUserClickEmoji: (that, emoji) ->
+    if $(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title")
+      $(that).siblings("button:has([data-emoji=#{emoji}])").attr("data-original-title").indexOf('me') > -1
+
   showEmojiMenu: ->
     if $(".emoji-menu").length
       if $(".emoji-menu").is ".is-visible"
@@ -105,7 +116,7 @@ class @AwardsHandler
     if origTitle
       authors = origTitle.split(', ')
     authors.push("me")
-    award_block.attr("title", authors.join(", "))
+    award_block.attr("data-original-title", authors.join(", "))
     @resetTooltip(award_block)
 
   resetTooltip: (award) ->
@@ -122,7 +133,7 @@ class @AwardsHandler
 
     nodes = []
     nodes.push(
-      "<button class='btn award-control js-emoji-btn has-tooltip active' title='me'>",
+      "<button class='btn award-control js-emoji-btn has-tooltip active' data-original-title='me'>",
       "<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>",
       "<span class='award-control-text js-counter'>1</span>",
       "</button>"
diff --git a/app/assets/javascripts/compare.js.coffee b/app/assets/javascripts/compare.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..f20992ead3ec6851819c5564499bcb08f87a95bf
--- /dev/null
+++ b/app/assets/javascripts/compare.js.coffee
@@ -0,0 +1,67 @@
+class @Compare
+  constructor: (@opts) ->
+    @source_loading = $ ".js-source-loading"
+    @target_loading = $ ".js-target-loading"
+
+    $('.js-compare-dropdown').each (i, dropdown) =>
+      $dropdown = $(dropdown)
+
+      $dropdown.glDropdown(
+        selectable: true
+        fieldName: $dropdown.data 'field-name'
+        filterable: true
+        id: (obj, $el) ->
+          $el.data 'id'
+        toggleLabel: (obj, $el) ->
+          $el.text().trim()
+        clicked: (e, el) =>
+          if $dropdown.is '.js-target-branch'
+            @getTargetHtml()
+          else if $dropdown.is '.js-source-branch'
+            @getSourceHtml()
+          else if $dropdown.is '.js-target-project'
+            @getTargetProject()
+      )
+
+    @initialState()
+
+  initialState: ->
+    @getSourceHtml()
+    @getTargetHtml()
+
+  getTargetProject: ->
+    $.ajax(
+      url: @opts.targetProjectUrl
+      data:
+        target_project_id:  $("input[name='merge_request[target_project_id]']").val()
+      beforeSend: ->
+        $('.mr_target_commit').empty()
+      success: (html) ->
+        $('.js-target-branch-dropdown .dropdown-content').html html
+    )
+
+  getSourceHtml: ->
+    @sendAjax(@opts.sourceBranchUrl, @source_loading, '.mr_source_commit',
+      ref: $("input[name='merge_request[source_branch]']").val()
+    )
+
+  getTargetHtml: ->
+    @sendAjax(@opts.targetBranchUrl, @target_loading, '.mr_target_commit',
+      target_project_id: $("input[name='merge_request[target_project_id]']").val()
+      ref: $("input[name='merge_request[target_branch]']").val()
+    )
+
+  sendAjax: (url, loading, target, data) ->
+    $target = $(target)
+
+    $.ajax(
+      url: url
+      data: data
+      beforeSend: ->
+        loading.show()
+        $target.empty()
+      success: (html) ->
+        loading.hide()
+        $target.html html
+        $('.js-timeago', $target).timeago()
+    )
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index e8d25591f63a70191d9de9ee122f977e6a38e5db..ee1d0fad28910531be2e57b1f5e27b528bb842af 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -57,14 +57,30 @@ class GitLabDropdownFilter
 
   filter: (search_text) ->
     data = @options.data()
-    results = data
 
-    if search_text isnt ""
-      results = fuzzaldrinPlus.filter(data, search_text,
-        key: @options.keys
-      )
+    if data?
+      results = data
 
-    @options.callback results
+      if search_text isnt ''
+        results = fuzzaldrinPlus.filter(data, search_text,
+          key: @options.keys
+        )
+
+      @options.callback results
+    else
+      elements = @options.elements()
+
+      if search_text
+        elements.each ->
+          $el = $(@)
+          matches = fuzzaldrinPlus.match($el.text().trim(), search_text)
+
+          if matches.length
+            $el.show()
+          else
+            $el.hide()
+      else
+        elements.show()
 
 class GitLabDropdownRemote
   constructor: (@dataEndpoint, @options) ->
@@ -123,7 +139,7 @@ class GitLabDropdown
     if _.isString(@filterInput)
       @filterInput = @getElement(@filterInput)
 
-    search_fields = if @options.search then @options.search.fields else [];
+    searchFields = if @options.search then @options.search.fields else [];
 
     if @options.data
       # If data is an array
@@ -147,7 +163,14 @@ class GitLabDropdown
         filterInputBlur: @filterInputBlur
         remote: @options.filterRemote
         query: @options.data
-        keys: @options.search.fields
+        keys: searchFields
+        elements: =>
+          selector = '.dropdown-content li:not(.divider)'
+
+          if @dropdown.find('.dropdown-toggle-page').length
+            selector = ".dropdown-page-one #{selector}"
+
+          return $(selector)
         data: =>
           return @fullData
         callback: (data) =>
@@ -376,7 +399,7 @@ class GitLabDropdown
 
       # Toggle the dropdown label
       if @options.toggleLabel
-        $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject)
+        $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject, el)
       if value?
         if !field.length and fieldName
           # Create hidden input for form
diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee
index 839e6ec2c0878934ffae2e983580a52703f433e8..9946249adbfed147a954d6f295ebfd5c46211897 100644
--- a/app/assets/javascripts/merge_request_tabs.js.coffee
+++ b/app/assets/javascripts/merge_request_tabs.js.coffee
@@ -73,7 +73,8 @@ class @MergeRequestTabs
       @expandView()
     else if action == 'diffs'
       @loadDiff($target.attr('href'))
-      @shrinkView()
+      if bp? and bp.getBreakpointSize() isnt 'lg'
+        @shrinkView()
     else if action == 'builds'
       @loadBuilds($target.attr('href'))
       @expandView()
diff --git a/app/assets/javascripts/milestone_select.js.coffee b/app/assets/javascripts/milestone_select.js.coffee
index f73127f49f0fa0119cc65e97b8252ec64a6bfd1c..6bd4e885a036cf5bdbb1a19a86e813baa187f2cb 100644
--- a/app/assets/javascripts/milestone_select.js.coffee
+++ b/app/assets/javascripts/milestone_select.js.coffee
@@ -85,15 +85,21 @@ class @MilestoneSelect
           # display:block overrides the hide-collapse rule
           $value.removeAttr('style')
         clicked: (selected) ->
+          page = $('body').data 'page'
+          isIssueIndex = page is 'projects:issues:index'
+          isMRIndex = page is page is 'projects:merge_requests:index'
+
           if $dropdown.hasClass 'js-filter-bulk-update'
             return
 
-          if $dropdown.hasClass('js-filter-submit')
+          if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
             if selected.name?
               selectedMilestone = selected.name
             else
               selectedMilestone = ''
             Issues.filterResults $dropdown.closest('form')
+          else if $dropdown.hasClass('js-filter-submit')
+            $dropdown.closest('form').submit()
           else
             selected = $selectbox
               .find('input[type="hidden"]')
diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee
index e17785112406c4afaa7da9072b027c84ccd032d3..860d4f438d0117b8efdca46a7725927663f4c7ee 100644
--- a/app/assets/javascripts/sidebar.js.coffee
+++ b/app/assets/javascripts/sidebar.js.coffee
@@ -4,6 +4,7 @@ expanded = 'page-sidebar-expanded'
 toggleSidebar = ->
   $('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
   $('header').toggleClass("header-collapsed header-expanded")
+  $('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
   $.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
 
   setTimeout ( ->
diff --git a/app/assets/javascripts/subscription.js.coffee b/app/assets/javascripts/subscription.js.coffee
index 084f0e0dc65c5922bc8fa5e51b37f045f88c260d..e4b7a3172ecd432340a863c1b0ab66880240a3e2 100644
--- a/app/assets/javascripts/subscription.js.coffee
+++ b/app/assets/javascripts/subscription.js.coffee
@@ -10,10 +10,10 @@ class @Subscription
     btn = $(event.currentTarget)
     action = btn.find('span').text()
     current_status = @subscription_status.attr('data-status')
-    btn.prop('disabled', true)
+    btn.addClass('disabled')
 
     $.post @url, =>
-      btn.prop('disabled', false)
+      btn.removeClass('disabled')
       status = if current_status == 'subscribed' then 'unsubscribed' else 'subscribed'
       @subscription_status.attr('data-status', status)
       action = if status == 'subscribed' then 'Unsubscribe' else 'Subscribe'
diff --git a/app/assets/javascripts/todos.js.coffee b/app/assets/javascripts/todos.js.coffee
index f39184777acedbe2e5b7e16a0bf080beb5f8f775..f16b735ece0da9c5708a29a7ecbc01efb50bf9b0 100644
--- a/app/assets/javascripts/todos.js.coffee
+++ b/app/assets/javascripts/todos.js.coffee
@@ -108,5 +108,10 @@ class @Todos
 
     uri + separator + key + '=' + value
 
-  goToTodoUrl: ->
-    Turbolinks.visit($(this).data('url'))
+  goToTodoUrl: (e)->
+    todoLink = $(this).data('url')
+    if e.metaKey
+      e.preventDefault()
+      window.open(todoLink,'_blank')
+    else
+      Turbolinks.visit(todoLink)
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 657c5f033c700335f74dd186f247c3f04f78645c..e8c0172680db63f9cf982898a42286008c026b58 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -7,6 +7,7 @@
   &:focus,
   &:active {
     outline: none;
+    background-color: $btn-active-gray;
     @include box-shadow($gl-btn-active-background);
   }
 }
@@ -27,7 +28,8 @@
     color: $color;
   }
 
-  &:active {
+  &:active,
+  &.active {
     @include box-shadow ($gl-btn-active-background);
 
     background-color: $dark;
@@ -61,7 +63,7 @@
 }
 
 @mixin btn-white {
-  @include btn-color($white-light, $border-white-light, $white-normal, $border-white-normal, $white-dark, $border-white-dark, #313236);
+  @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
 }
 
 .btn {
@@ -218,3 +220,26 @@
     margin-right: 5px;
   }
 }
+
+.btn-text-field {
+  width: 100%;
+  text-align: left;
+  padding: 6px 16px;
+  border-color: $border-color;
+  color: $btn-placeholder-gray;
+  background-color: $background-color;
+
+  &:hover,
+  &:active,
+  &:focus {
+    cursor: text;
+    box-shadow: none;
+    border-color: $border-color;
+    color: $btn-placeholder-gray;
+    background-color: $background-color;
+  }
+}
+
+.btn-file-option {
+  background: linear-gradient(180deg, $white-light 25%, $gray-light 100%);
+}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 82dc1acbd017c96ff2144d681a79605ab5705cdb..ba6c7930cdcd9e47d4be70977f2ad82f2985073b 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -248,7 +248,7 @@
 
 .dropdown-title {
   position: relative;
-  padding: 0 0 15px;
+  padding: 0 25px 15px;
   margin: 0 10px 10px;
   font-weight: 600;
   line-height: 1;
@@ -275,7 +275,7 @@
 }
 
 .dropdown-menu-close {
-  right: 7px;
+  right: 5px;
   width: 20px;
   height: 20px;
   top: -1px;
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index b15f4e7bd5ed80cb6d4c31718bd245b8dc0f5e3e..789df42fb66afc5b3051a96edb2b9d5deeecfa2a 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -15,12 +15,13 @@
 
   .file-title {
     position: relative;
-    background: $background-color;
+    background-color: $background-color;
     border-bottom: 1px solid $border-color;
     margin: 0;
     text-align: left;
     padding: 10px $gl-padding;
     word-wrap: break-word;
+    border-radius: 3px 3px 0 0;
 
     .file-actions {
       float: right;
@@ -49,7 +50,7 @@
       }
     }
 
-    a {
+    a:not(.btn) {
       color: $gl-dark-link-color;
     }
 
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index fa9038ebaca12b0cd9b2efa1e213990e16709629..c83cf881596f646665b2cf7830a87871cb7f1513 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -33,15 +33,10 @@
       background: $color;
     }
 
-    .complex-sidebar .nav-primary {
-      border-right: 1px solid lighten($color, 3%);
-    }
-
     .sidebar-wrapper {
       background: $color-darker;
 
       .sidebar-user {
-        border-top: 1px solid lighten($color, 3%);
         background: $color-darker;
         color: $color-light;
 
@@ -67,6 +62,7 @@
 
         .count {
           color: $color-light;
+          background: $color-dark;
         }
       }
 
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 724980b2208fe5a4317c55289beff1949445907f..b3397d160165ceb4069d4a512416642bd0ab9d1d 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -123,11 +123,11 @@ header {
 }
 
 @mixin collapsed-header {
-  margin-left: 40px;
+  margin-left: $sidebar_collapsed_width;
 }
 
 .header-collapsed {
-  margin-left: 40px;
+  margin-left: $sidebar_collapsed_width;
 
   @media (min-width: $screen-md-min) {
     @include collapsed-header;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 1d49249dd804087857cfac68d07e2179ffb43de1..18189e985c46f546dfadffd37b1d6ba3b098c0c5 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -144,7 +144,7 @@
     }
 
     a {
-      padding: 7px 12px;
+      padding: 7px 15px;
       font-size: $gl-font-size;
       line-height: 24px;
       color: $gray;
@@ -169,12 +169,10 @@
       }
 
       .count {
-        &:before {
-          content: '(';
-        }
-        &:after {
-          content: ')';
-        }
+        float: right;
+        background: #eee;
+        padding: 0 8px;
+        @include border-radius(6px);
       }
 
       &.back-link i {
@@ -193,27 +191,6 @@
   }
 }
 
-.expand-nav a {
-  color: $gl-icon-color;
-  width: 60px;
-  position: fixed;
-  top: 0;
-  left: 0;
-  font-size: 20px;
-  background: #fff;
-  height: 59px;
-  text-align: center;
-  line-height: 59px;
-  border-bottom: 1px solid #eee;
-  transition-duration: .3s;
-  outline: none;
-  z-index: 100;
-
-  &:hover {
-    text-decoration: none;
-  }
-}
-
 .collapse-nav a {
   width: $sidebar_width;
   position: fixed;
@@ -233,12 +210,55 @@
 }
 
 .page-sidebar-collapsed {
+  padding-left: $sidebar_collapsed_width;
+
   .sidebar-wrapper {
-    display: none;
+    width: $sidebar_collapsed_width;
+
+    .header-logo {
+      width: $sidebar_collapsed_width;
+
+      a {
+        padding-left: ($sidebar_collapsed_width - 36) / 2;
+
+        .gitlab-text-container {
+          display: none;
+        }
+      }
+    }
+
+    .nav-sidebar {
+      width: $sidebar_collapsed_width;
+
+      li {
+        width: auto;
+
+        a {
+          span {
+            display: none;
+          }
+        }
+      }
+    }
+
+    .collapse-nav a {
+      width: $sidebar_collapsed_width;
+    }
+
+    .sidebar-user {
+      padding-left: ($sidebar_collapsed_width - 36) / 2;
+      width: $sidebar_collapsed_width;
+
+      .username {
+        display: none;
+      }
+    }
   }
 }
 
 .page-sidebar-expanded {
+  padding-left: $sidebar_collapsed_width;
+
   @media (min-width: $screen-md-min) {
     padding-left: $sidebar_width;
   }
@@ -289,48 +309,3 @@
     padding-right: $sidebar_collapsed_width;
   }
 }
-
-.complex-sidebar {
-  display: inline-block;
-
-  .nav-primary {
-    width: 61px;
-    float: left;
-    height: 100vh;
-
-    .nav-sidebar {
-      width: 60px;
-
-      li a {
-        width: 60px;
-
-        span {
-          display: none;
-        }
-      }
-    }
-  }
-
-  .nav-secondary {
-    $nav-secondary-width: 168px;
-
-    float: left;
-    width: $nav-secondary-width;
-
-    .nav-sidebar {
-      width: $nav-secondary-width;
-
-      li {
-        width: $nav-secondary-width;
-
-        a {
-          width: $nav-secondary-width;
-
-          i {
-            display: none;
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index aa244fe548da7fe776ad78925c84c7bd8e58b517..b91f2f6f898dfc7ef5e7fd265cb8918c2a303e14 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -14,10 +14,6 @@
       background: $row-hover;
     }
 
-    &:last-child {
-      border-bottom: none;
-    }
-
     .avatar {
       margin-right: 15px;
     }
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 8d3ad934a505124e52850d3b933f5e727733f7ed..1ebbd9b0e57092f55c47ce31084093f7c1ae2194 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -10,10 +10,10 @@ $gutter_inner_width: 258px;
 /*
  * UI elements
  */
-$border-color:       #efeff1;
+$border-color:       #e5e5e5;
 $focus-border-color: #3aabf0;
 $table-border-color: #eef0f2;
-$background-color:   #faf9f9;
+$background-color:   #fafafa;
 
 /*
  * Text
@@ -81,7 +81,7 @@ $provider-btn-not-active-color: #4688f1;
 
 $white-light: #fff;
 $white-normal: #ededed;
-$white-dark: #ededed;
+$white-dark: #ececec;
 
 $gray-light: #faf9f9;
 $gray-normal: #f5f5f5;
@@ -108,6 +108,8 @@ $red-light: #e52c5a;
 $red-normal: #d22852;
 $red-dark: darken($red-normal, 5%);
 
+$black-transparent: rgba(0, 0, 0, 0.3);
+
 $border-white-light: #f1f2f4;
 $border-white-normal: #d6dae2;
 $border-white-dark: #c6cacf;
@@ -150,15 +152,22 @@ $gl-success: $green-normal;
 $gl-info: $blue-normal;
 $gl-warning: $orange-normal;
 $gl-danger: $red-normal;
-$gl-btn-active-background: rgba(0, 0, 0, 0.12);
-$gl-btn-active-gradient: inset 0 0 4px $gl-btn-active-background;
+$gl-btn-active-background: rgba(0, 0, 0, 0.16);
+$gl-btn-active-gradient: inset 0 2px 3px $gl-btn-active-background;
 
 /*
  * Commit Diff Colors
  */
 $added: #63c363;
 $deleted: #f77;
-
+$line-added: #ecfdf0;
+$line-added-dark: #c7f0d2;
+$line-removed: #fbe9eb;
+$line-removed-dark: #fac5cd;
+$line-number-old: #f9d7dc;
+$line-number-new: #ddfbe6;
+$match-line: #fafafa;
+$table-border-gray: #f0f0f0;
 /*
  * Fonts
  */
@@ -191,6 +200,13 @@ $dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 15%);
 $dropdown-toggle-icon-color: #c4c4c4;
 $dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color;
 
+/*
+* Buttons
+*/
+$btn-active-gray: #ececec;
+$btn-placeholder-gray: #c7c7c7;
+$btn-white-active: #848484;
+
 /*
  *  Award emoji
  */
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index b90c95c62d1378d1e9d3db0ce684b6c23321b452..c482a1258f7d45f77e3db5831a357027e548848e 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -6,7 +6,7 @@
   }
 
   .diff-line-num, .diff-line-num a {
-    color: rgba(0, 0, 0, 0.3);
+    color: $black-transparent;
   }
 
   // Code itself
@@ -30,7 +30,7 @@
     }
 
     .line_content.match {
-      color: rgba(0, 0, 0, 0.3);
+      color: $black-transparent;
       background: rgba(255, 255, 255, 0.4);
     }
   }
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index 8c1b0cd84ecc84beb82d6f71c312dde55f8414c3..28331f59754f2684291473e392dd3d9fc67eb9af 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -6,12 +6,12 @@
   }
 
   .diff-line-num, .diff-line-num a {
-    color: rgba(0, 0, 0, 0.3);
+    color: $black-transparent;
   }
 
   // Code itself
   pre.code, .diff-line-num {
-    border-color: $border-color;
+    border-color: $table-border-gray;
   }
 
   &, pre.code, .line_holder .line_content {
@@ -23,36 +23,36 @@
   .line_holder {
     .diff-line-num {
       &.old {
-        background: #fdd;
-        border-color: #f1c0c0;
+        background-color: $line-number-old;
+        border-color: $line-removed-dark;
       }
 
       &.new {
-        background: #dbffdb;
-        border-color: #c1e9c1;
+        background-color: $line-number-new;
+        border-color: $line-added-dark;
       }
     }
 
     .line_content {
       &.old {
-        background: #ffecec;
+        background: $line-removed;
 
         span.idiff {
-          background-color: #f8cbcb;
+          background-color: $line-removed-dark;
         }
       }
 
       &.new {
-        background: #eaffea;
+        background-color: $line-added;
 
         span.idiff {
-          background-color: #a6f3a6;
+          background-color: $line-added-dark;
         }
       }
 
       &.match {
-        color: rgba(0, 0, 0, 0.3);
-        background: #fafafa;
+        color: $black-transparent;
+        background: $match-line;
       }
     }
   }
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index 082911bd118c1ff0b17773b7a73dd55774cd0257..358d2f4ab9d9d73c60880c62f0994701946e20d2 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -20,6 +20,8 @@
   margin: 0;
   padding: 0;
   margin-top: 10px;
+  word-break: normal;
+  white-space: pre-wrap;
 }
 
 .commit-info-row {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 8272615768d90a3d3d09af7a9c84a8cc807829f6..6453c91d955069866bea7c0873c9f3fbc26e8306 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -47,6 +47,7 @@ li.commit {
 
     .commit_short_id {
       min-width: 65px;
+      color: $gl-dark-link-color;
       font-family: $monospace_font;
     }
 
@@ -88,6 +89,10 @@ li.commit {
       padding: 0;
       margin: 0;
     }
+
+    a {
+      color: $gl-dark-link-color;
+    }
   }
 
   .commit-row-info {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 939555bb260fa2350c483dec767e7aea0a63bd7d..d0855f66911d127a1a39eeadd6620f586e0db203 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -2,6 +2,7 @@
 .diff-file {
   border: 1px solid $border-color;
   margin-bottom: $gl-padding;
+  border-radius: 3px;
 
   .diff-header {
     position: relative;
@@ -10,6 +11,7 @@
     padding: 10px 16px;
     color: #555;
     z-index: 10;
+    border-radius: 3px 3px 0 0;
 
     .diff-title {
       font-family: $monospace_font;
@@ -31,6 +33,7 @@
     overflow-y: hidden;
     background: #fff;
     color: #333;
+    border-radius: 0 0 3px 3px;
 
     .unfold {
       cursor: pointer;
@@ -109,6 +112,10 @@
         display: table-cell;
       }
     }
+
+    .text-file.diff-wrap-lines table .line_holder td span {
+      white-space: pre-wrap;
+    }
   }
   .image {
     background: #ddd;
@@ -321,6 +328,16 @@
   float: right;
 }
 
+.diffs {
+  .content-block {
+    border-bottom: none;
+  }
+}
+
+.files-changed {
+  border-bottom: none;
+}
+
 // Mobile
 @media (max-width: 480px) {
   .diff-title {
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index bd224705f04aa29366c410542713eccabdfdc39b..604f1700cf840543786e51c956b68d272be2966e 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -59,6 +59,9 @@
   position: relative;
   overflow-y: auto;
   padding: 15px;
+  .form-actions {
+    margin: -$gl-padding+1;
+  }
 }
 
 body.modal-open {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 4e02ec4e89164475eea0bd8f50fcb0ce866a33c6..3e0a3140be77a6d99f3be7e31fbc3b611ae1b467 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -49,6 +49,15 @@
 }
 
 .label-row {
+  .label-name {
+    display: inline-block;
+    width: 200px;
+
+    @media (max-width: $screen-xs-min) {
+      display: block;
+    }
+  }
+
   .label {
     padding: 9px;
     font-size: 14px;
@@ -69,3 +78,52 @@
   background-color: $gl-danger;
   color: $white-light;
 }
+
+.manage-labels-list {
+
+  .prepend-left-10 {
+    display: inline-block;
+    width: 40%;
+    vertical-align: middle;
+
+    @media (max-width: $screen-xs-min) {
+      display: block;
+      width: 100%;
+      margin-left: 0;
+      padding: 10px 0;
+    }
+  }
+
+  .pull-info-right {
+    float: right;
+
+    @media (max-width: $screen-xs-min) {
+      float: none;
+    }
+
+    .action-buttons {
+      border-color: transparent;
+      padding: 6px;
+      color: $gl-text-color;
+
+      &.subscribe-button {
+        padding-left: 0;
+      }
+    }
+
+    i {
+      color: $gl-text-color;
+    }
+
+    .append-right-20 {
+      a {
+        color: $gl-text-color;
+      }
+
+      @media (max-width: $screen-xs-min) {
+        display: block;
+        margin-bottom: 10px;
+      }
+    }
+  }
+}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 1c6a420897433d7f5d5f1e6b421ee1339365cff4..b79335eab9155bb336eca8d3e2b2c7fa64b27fac 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -123,6 +123,8 @@
 
 .mr_source_commit,
 .mr_target_commit {
+  margin-bottom: 0;
+
   .commit {
     margin: 0;
     padding: 2px 0;
@@ -174,10 +176,6 @@
   display: none;
 }
 
-.merge-request-form .select2-container {
-  width: 250px !important;
-}
-
 #modal_merge_info .modal-dialog {
   width: 600px;
 
@@ -200,3 +198,76 @@
     overflow-x: scroll;
   }
 }
+
+.panel-new-merge-request {
+  .panel-heading {
+    padding: 5px 10px;
+    font-weight: 600;
+    line-height: 25px;
+  }
+
+  .panel-body {
+    padding: 10px 5px;
+  }
+
+  .panel-footer {
+    padding: 5px 10px;
+  }
+
+  .commit {
+    .commit-row-title {
+      margin-bottom: 4px;
+    }
+
+    .avatar {
+      width: 20px;
+      height: 20px;
+      margin-right: 5px;
+    }
+
+    .commit-row-info {
+      line-height: 20px;
+    }
+  }
+
+  .btn-clipboard {
+    margin-right: 5px;
+    padding: 0;
+    background: transparent;
+  }
+
+  .ci-status-link {
+    margin-right: 5px;
+  }
+}
+
+.merge-request-select {
+  padding-left: 5px;
+  padding-right: 5px;
+  margin-bottom: 10px;
+
+  &:last-child {
+    margin-bottom: 0;
+  }
+
+  @media (min-width: $screen-sm-min) {
+    float: left;
+    width: 50%;
+    margin-bottom: 0;
+  }
+
+  .dropdown-menu-toggle {
+    width: 100%;
+  }
+
+  .dropdown-menu {
+    left: 5px;
+    right: 5px;
+    width: auto;
+  }
+}
+
+.issuable-form-select-holder {
+  display: inline-block;
+  width: 250px;
+}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index a909776b4377faab1d1c192f9465eacb98fb8003..4d4d508396d7b1ad429950e9d5d4a2bb37f04bac 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -1,10 +1,10 @@
 /**
  * Note Form
  */
-.reply-btn {
-  @extend .btn-primary;
-  margin: 10px $gl-padding;
+.comment-btn {
+  @extend .btn-create;
 }
+
 .diff-file .diff-content {
   tr.line_holder:hover > td .line_note_link {
     opacity: 1.0;
@@ -113,13 +113,12 @@
 .discussion-body,
 .diff-file {
   .notes .note {
-    border-color: #ddd;
     padding: 10px 15px;
   }
 
   .discussion-reply-holder {
-    background: $background-color;
-    border-top: 1px solid $border-color;
+    background-color: $white-light;
+    padding: 10px 16px;
   }
 }
 
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index aca86457c7030e8965a3b2d47087f4091de54b36..7295fe51121e5346bd9d6479286a1be742eac40a 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -58,6 +58,7 @@ ul.notes {
   .note {
     display: block;
     position: relative;
+    border-bottom: 1px solid $table-border-gray;
 
     &.is-editting {
       .note-header,
@@ -117,9 +118,6 @@ ul.notes {
       padding-bottom: 3px;
     }
 
-    &:last-child {
-      border-bottom: 1px solid $border-color;
-    }
   }
 }
 
@@ -137,14 +135,14 @@ ul.notes {
   font-family: $regular_font;
 
   td {
-    border: 1px solid #ddd;
+    border: 1px solid $table-border-gray;
     border-left: none;
 
     &.notes_line {
       vertical-align: middle;
       text-align: center;
       padding: 10px 0;
-      background: #fff;
+      background: $background-color;
       color: $text-color;
     }
     &.notes_line2 {
@@ -175,9 +173,6 @@ ul.notes {
     }
   }
 
-  .author_link {
-    font-weight: 600;
-  }
 }
 
 .note-headline-light,
@@ -203,14 +198,26 @@ ul.notes {
   line-height: 24px;
 
   .fa {
+    color: $notes-action-color;
     position: relative;
     top: 1px;
     font-size: 17px;
   }
 
-  .fa-trash-o {
-    top: 0;
-    font-size: 16px;
+  &.js-note-delete {
+    i {
+      &:hover {
+        color: $gl-text-red;
+      }
+    }
+  }
+
+  &.js-note-edit {
+    i {
+      &:hover {
+        color: $gl-link-color;
+      }
+    }
   }
 }
 
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index c81cb85dc1befddd12fdd311ffd6203df6ce1ac6..97d53acde941ee1cf27788a7a736d244047352da 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -47,6 +47,16 @@ class ApplicationController < ActionController::Base
         email: current_user.email,
         username: current_user.username,
       )
+
+      Raven.tags_context(program: sentry_program_context)
+    end
+  end
+
+  def sentry_program_context
+    if Sidekiq.server?
+      'sidekiq'
+    else
+      'rails'
     end
   end
 
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 49064f5d50570f6c4d6463797fbb87ae8c15a734..ae613f5e093c57d910b660f82f1ebd79a37ee204 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -207,20 +207,20 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     #This is always source
     @source_project = @merge_request.nil? ? @project : @merge_request.source_project
     @commit = @repository.commit(params[:ref]) if params[:ref].present?
+    render layout: false
   end
 
   def branch_to
     @target_project = selected_target_project
     @commit = @target_project.commit(params[:ref]) if params[:ref].present?
+    render layout: false
   end
 
   def update_branches
     @target_project = selected_target_project
     @target_branches = @target_project.repository.branch_names
 
-    respond_to do |format|
-      format.js
-    end
+    render layout: false
   end
 
   def ci_status
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 820d69c230b9ed2aceb8893aa16ebc43947dc984..9e59a295fc469fa39f01b68e585b69412af822b9 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -27,9 +27,9 @@ module BlobHelper
                                      link_opts)
 
     if !on_top_of_branch?(project, ref)
-      button_tag "Edit", class: "btn btn-default disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
+      button_tag "Edit", class: "btn disabled has-tooltip btn-file-option", title: "You can only edit files when you are on a branch", data: { container: 'body' }
     elsif can_edit_blob?(blob, project, ref)
-      link_to "Edit", edit_path, class: 'btn'
+      link_to "Edit", edit_path, class: 'btn btn-file-option'
     elsif can?(current_user, :fork_project, project)
       continue_params = {
         to:     edit_path,
@@ -38,7 +38,7 @@ module BlobHelper
       }
       fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
 
-      link_to "Edit", fork_path, class: 'btn', method: :post
+      link_to "Edit", fork_path, class: 'btn btn-file-option', method: :post
     end
   end
 
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index bde0799f3dea5a94b111ed95cbc56074bf9ed8e4..35ba543cef12f42d10d781e1eb91c27017d9f60c 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -28,7 +28,7 @@ module CommitsHelper
 
   def commit_to_html(commit, project, inline = true)
     template = inline ? "inline_commit" : "commit"
-    escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil?
+    render "projects/commits/#{template}", commit: commit, project: project unless commit.nil?
   end
 
   # Breadcrumb links for a Project and, if applicable, a tree path
@@ -117,7 +117,7 @@ module CommitsHelper
       end
     end
     link_to(
-      "Browse Files »",
+      "Browse Files",
       namespace_project_tree_path(project.namespace, project, commit),
       class: "pull-right"
     )
@@ -197,7 +197,7 @@ module CommitsHelper
     link_to(
       namespace_project_blob_path(project.namespace, project,
                                   tree_join(commit_sha, diff.new_path)),
-      class: 'btn view-file js-view-file'
+      class: 'btn view-file js-view-file btn-file-option'
     ) do
       raw('View file @') + content_tag(:span, commit_sha[0..6],
                                        class: 'commit-short-id')
diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6a43be2cf3ef14049e07f6b38ed9a4ab05e0fe13
--- /dev/null
+++ b/app/helpers/form_helper.rb
@@ -0,0 +1,18 @@
+module FormHelper
+  def form_errors(model)
+    return unless model.errors.any?
+
+    pluralized = 'error'.pluralize(model.errors.count)
+    headline   = "The form contains the following #{pluralized}:"
+
+    content_tag(:div, class: 'alert alert-danger', id: 'error_explanation') do
+      content_tag(:h4, headline) <<
+      content_tag(:ul) do
+        model.errors.full_messages.
+          map { |msg| content_tag(:li, msg) }.
+          join.
+          html_safe
+      end
+    end
+  end
+end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 2f760af02fd3939d686741066e0134bfc3fbe2e3..3a45205563e72181831f6e8efe0e3381d301c519 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -116,29 +116,6 @@ module GitlabMarkdownHelper
     end
   end
 
-  MARKDOWN_TIPS = [
-    "End a line with two or more spaces for a line-break, or soft-return",
-    "Inline code can be denoted by `surrounding it with backticks`",
-    "Blocks of code can be denoted by three backticks ``` or four leading spaces",
-    "Emoji can be added by :emoji_name:, for example :thumbsup:",
-    "Notify other participants using @user_name",
-    "Notify a specific group using @group_name",
-    "Notify the entire team using @all",
-    "Reference an issue using a hash, for example issue #123",
-    "Reference a merge request using an exclamation point, for example MR !123",
-    "Italicize words or phrases using *asterisks* or _underscores_",
-    "Bold words or phrases using **double asterisks** or __double underscores__",
-    "Strikethrough words or phrases using ~~two tildes~~",
-    "Make a bulleted list using + pluses, - minuses, or * asterisks",
-    "Denote blockquotes using > at the beginning of a line",
-    "Make a horizontal line using three or more hyphens ---, asterisks ***, or underscores ___"
-  ].freeze
-
-  # Returns a random markdown tip for use as a textarea placeholder
-  def random_markdown_tip
-    MARKDOWN_TIPS.sample
-  end
-
   private
 
   # Return +text+, truncated to +max_chars+ characters, excluding any HTML
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 24b90fef4fe94169f984f1450e2fa4195ed17121..bcf8639c82960660c13736a43b8727996cdc91a2 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -115,17 +115,32 @@ module IssuesHelper
     icon('eye-slash') if issue.confidential?
   end
 
-  def emoji_icon(name, unicode = nil, aliases = [])
+  def emoji_icon(name, unicode = nil, aliases = [], sprite: true)
     unicode ||= Emoji.emoji_filename(name) rescue ""
 
-    content_tag :div, "",
-      class: "icon emoji-icon emoji-#{unicode}",
-      title: name,
-      data: {
-        aliases: aliases.join(' '),
-        emoji: name,
-        unicode_name: unicode
-      }
+    data = {
+      aliases: aliases.join(" "),
+      emoji: name,
+      unicode_name: unicode
+    }
+
+    if sprite
+      # Emoji icons for the emoji menu, these use a spritesheet.
+      content_tag :div, "",
+        class: "icon emoji-icon emoji-#{unicode}",
+        title: name,
+        data: data
+    else 
+      # Emoji icons displayed separately, used for the awards already given
+      # to an issue or merge request.
+      content_tag :img, "",
+        class: "icon emoji",
+        title: name,
+        height: "20px",
+        width: "20px",
+        src: url_to_image("#{unicode}.png"),
+        data: data
+    end
   end
 
   def emoji_author_list(notes, current_user)
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 698f90cb27ae9df09d6f22e9e5465e39cd0ed406..95072b5373fd21a4433d1d37b98cdbc57ec71544 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -69,10 +69,7 @@ module NotesHelper
       line_type:     line_type
     }
 
-    button_tag class: 'btn btn-nr reply-btn js-discussion-reply-button',
-               data: data, title: 'Add a reply' do
-      link_text = icon('comment')
-      link_text << ' Reply'
-    end
+    button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button',
+                           data: data, title: 'Add a reply'
   end
 end
diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml
index 3bc1b24b5e2067bebdeb20392d91a0e46c255ba9..06be1a53318a068f98e17ec6f39f48af82c109f1 100644
--- a/app/views/abuse_reports/new.html.haml
+++ b/app/views/abuse_reports/new.html.haml
@@ -3,11 +3,9 @@
 %p Please use this form to report users who create spam issues, comments or behave inappropriately.
 %hr
 = form_for @abuse_report, html: { class: 'form-horizontal js-quick-submit js-requires-input'} do |f|
+  = form_errors(@abuse_report)
+
   = f.hidden_field :user_id
-  - if @abuse_report.errors.any?
-    .alert.alert-danger
-      - @abuse_report.errors.full_messages.each do |msg|
-        %p= msg
   .form-group
     = f.label :user_id, class: 'control-label'
     .col-sm-10
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index 6f325914d14f048a0574546cc41a95d5b0f0bf97..d88f3ad314d01c51bca8e6ac56a20b4296e5cff9 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -1,8 +1,5 @@
 = form_for @appearance, url: admin_appearances_path, html: { class: 'form-horizontal'} do |f|
-  - if @appearance.errors.any?
-    .alert.alert-danger
-      - @appearance.errors.full_messages.each do |msg|
-        %p= msg
+  = form_errors(@appearance)
 
   %fieldset.sign-in
     %legend
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index de86dacbb1242e3f9188750da30cf628f8baafca..a8cca1a81cb3537728aa505f8320cc2a5878e738 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -1,9 +1,5 @@
 = form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
-  - if @application_setting.errors.any?
-    #error_explanation
-      .alert.alert-danger
-        - @application_setting.errors.full_messages.each do |msg|
-          %p= msg
+  = form_errors(@application_setting)
 
   %fieldset
     %legend Visibility and Access Controls
diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml
index e18f7b499dd3fc9543fb25a5362b0164c44c2259..4aacbb8cd7791c01485068310af6aa145e8c8e28 100644
--- a/app/views/admin/applications/_form.html.haml
+++ b/app/views/admin/applications/_form.html.haml
@@ -1,9 +1,6 @@
 = form_for [:admin, @application], url: @url, html: {class: 'form-horizontal', role: 'form'} do |f|
-  - if application.errors.any?
-    .alert.alert-danger
-      %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
-      - application.errors.full_messages.each do |msg|
-        %p= msg
+  = form_errors(application)
+
   = content_tag :div, class: 'form-group' do
     = f.label :name, class: 'col-sm-2 control-label'
     .col-sm-10
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index b748460a9f7a46f49978c163bec58b9e784fe371..6b157abf8422db584d0fdcc12dd79ccb6411c677 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -4,10 +4,8 @@
     = render_broadcast_message(@broadcast_message.message.presence || "Your message here")
 
 = form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-quick-submit js-requires-input'} do |f|
-  -if @broadcast_message.errors.any?
-    .alert.alert-danger
-      - @broadcast_message.errors.full_messages.each do |msg|
-        %p= msg
+  = form_errors(@broadcast_message)
+
   .form-group
     = f.label :message, class: 'control-label'
     .col-sm-10
diff --git a/app/views/admin/deploy_keys/new.html.haml b/app/views/admin/deploy_keys/new.html.haml
index 5b46b3222a9cc5a5ffcdf21c72451a0f739f39be..15aa059c93d0b5f82a329537e92a02d7189fd452 100644
--- a/app/views/admin/deploy_keys/new.html.haml
+++ b/app/views/admin/deploy_keys/new.html.haml
@@ -4,11 +4,7 @@
 
 %div
   = form_for [:admin, @deploy_key], html: { class: 'deploy-key-form form-horizontal' } do |f|
-    -if @deploy_key.errors.any?
-      .alert.alert-danger
-        %ul
-          - @deploy_key.errors.full_messages.each do |msg|
-            %li= msg
+    = form_errors(@deploy_key)
 
     .form-group
       = f.label :title, class: "control-label"
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index 7f2b1cd235d990ce976cbea59d6dbef2fe0932a3..0cc405401cf5450f712941b31eed7a21af9420a3 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -1,8 +1,5 @@
 = form_for [:admin, @group], html: { class: "form-horizontal" } do |f|
-  - if @group.errors.any?
-    .alert.alert-danger
-      %span= @group.errors.full_messages.first
-
+  = form_errors(@group)
   = render 'shared/group_form', f: f
 
   .form-group.group-description-holder
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index 53b3cd04c682964175ec96071a94b82a4d998f2a..ad952052f253efc3ef1ab423502621d7cb677e37 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -10,10 +10,8 @@
 
 
 = form_for @hook, as: :hook, url: admin_hooks_path, html: { class: 'form-horizontal' } do |f|
-  -if @hook.errors.any?
-    .alert.alert-danger
-      - @hook.errors.full_messages.each do |msg|
-        %p= msg
+  = form_errors(@hook)
+
   .form-group
     = f.label :url, "URL:", class: 'control-label'
     .col-sm-10
diff --git a/app/views/admin/identities/_form.html.haml b/app/views/admin/identities/_form.html.haml
index 3a7885582262b02dd5bb9414952dbb0c1af2aae1..112a201fafa7bc975b5335e244b00a01e887e206 100644
--- a/app/views/admin/identities/_form.html.haml
+++ b/app/views/admin/identities/_form.html.haml
@@ -1,9 +1,5 @@
 = form_for [:admin, @user, @identity], html: { class: 'form-horizontal fieldset-form' } do |f|
-  - if @identity.errors.any?
-    #error_explanation
-      .alert.alert-danger
-        - @identity.errors.full_messages.each do |msg|
-          %p= msg
+  = form_errors(@identity)
 
   .form-group
     = f.label :provider, class: 'control-label'
diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml
index 8c6b389bf157ac2c103861ae794ffe631bd71f55..448aa95354818b0ac87f39ac546984585a2ff99e 100644
--- a/app/views/admin/labels/_form.html.haml
+++ b/app/views/admin/labels/_form.html.haml
@@ -1,11 +1,5 @@
 = form_for [:admin, @label], html: { class: 'form-horizontal label-form js-requires-input' } do |f|
-  -if @label.errors.any?
-    .row
-      .col-sm-offset-2.col-sm-10
-        .alert.alert-danger
-          - @label.errors.full_messages.each do |msg|
-            %span= msg
-            %br
+  = form_errors(@label)
 
   .form-group
     = f.label :title, class: 'control-label'
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index d2527ede99559ea4980af70ea43239fb9f460889..b05fdbd5552c50d1ad8705b12c740eb56c332c79 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -1,10 +1,6 @@
 .user_new
   = form_for [:admin, @user], html: { class: 'form-horizontal fieldset-form' } do |f|
-    -if @user.errors.any?
-      #error_explanation
-        .alert.alert-danger
-          - @user.errors.full_messages.each do |msg|
-            %p= msg
+    = form_errors(@user)
 
     %fieldset
       %legend Account
diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml
index 906b0676150cfa50173099b6dd7058b74f306728..5c98265727a13e899f315e14d7705efd33e86b7e 100644
--- a/app/views/doorkeeper/applications/_form.html.haml
+++ b/app/views/doorkeeper/applications/_form.html.haml
@@ -1,9 +1,5 @@
 = form_for application, url: doorkeeper_submit_path(application), html: {role: 'form'} do |f|
-  - if application.errors.any?
-    .alert.alert-danger
-      %ul
-        - application.errors.full_messages.each do |msg|
-          %li= msg
+  = form_errors(application)
 
   .form-group
     = f.label :name, class: 'label-light'
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index ea5a0358392911d17750a6b19987c0e4222a4dfb..a698cbbe9dbd4a5b67ca118cf2fa16b8e1ec8867 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -5,9 +5,7 @@
     Group settings
   .panel-body
     = form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f|
-      - if @group.errors.any?
-        .alert.alert-danger
-          %span= @group.errors.full_messages.first
+      = form_errors(@group)
       = render 'shared/group_form', f: f
 
       .form-group
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 30ab8aeba13e9d1706bf189bdf98190ae0e7395d..2b8bc269e645c117c15955469075b156f3406c77 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -6,10 +6,7 @@
 %hr
 
 = form_for @group, html: { class: 'group-form form-horizontal' } do |f|
-  - if @group.errors.any?
-    .alert.alert-danger
-      %span= @group.errors.full_messages.first
-
+  = form_errors(@group)
   = render 'shared/group_form', f: f, autofocus: true
 
   .form-group.group-description-holder
diff --git a/app/views/layouts/_collapse_button.html.haml b/app/views/layouts/_collapse_button.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..2ed51d87ca1332d8c9d7b34ba3a7219b662a53f9
--- /dev/null
+++ b/app/views/layouts/_collapse_button.html.haml
@@ -0,0 +1,4 @@
+- if nav_menu_collapsed?
+  = link_to icon('angle-right'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
+- else
+  = link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 9be36273c7d1d842e311eced82e00a1c1d5dbd1c..c799e9c588d60fdd383e0f835641d1319a43d45c 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,7 +1,5 @@
 .page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
   = render "layouts/broadcast"
-  .expand-nav
-    = link_to icon('bars'), '#', class: 'toggle-nav-collapse', title: "Open sidebar"
   .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
     .header-logo
       %a#logo
@@ -10,19 +8,15 @@
         .gitlab-text-container
           %h3 GitLab
 
-    - primary_sidebar = current_user ? 'dashboard' : 'explore'
-
-    - if defined?(sidebar) && sidebar && sidebar != primary_sidebar
-      .complex-sidebar
-        .nav-primary
-          = render "layouts/nav/#{primary_sidebar}"
-        .nav-secondary
-          = render "layouts/nav/#{sidebar}"
+    - if defined?(sidebar) && sidebar
+      = render "layouts/nav/#{sidebar}"
+    - elsif current_user
+      = render 'layouts/nav/dashboard'
     - else
-      = render "layouts/nav/#{primary_sidebar}"
+      = render 'layouts/nav/explore'
 
     .collapse-nav
-      = link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Hide sidebar"
+      = render partial: 'layouts/collapse_button'
     - if current_user
       = link_to current_user, class: 'sidebar-user', title: "Profile" do
         = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 22d1d4d85977e928e5c8a04203aa8ee6ebe9fba0..280a1b93729ba405bce73f7e3eb0509cbcf1e4ee 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -95,7 +95,7 @@
           Spam Logs
           %span.count= number_with_delimiter(SpamLog.count(:all))
 
-  = nav_link(controller: :application_settings) do
+  = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
     = link_to admin_application_settings_path, title: 'Settings' do
       = icon('cogs fw')
       %span
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index f1052eadf81cca381977debd17d2b856b50dda63..5cef652da14b7a44f1801f2f15127089d8fcf623 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -15,12 +15,12 @@
       = icon('dashboard fw')
       %span
         Activity
-  = nav_link(path: ['dashboard/groups#index', 'explore/groups#index']) do
+  = nav_link(controller: :groups) do
     = link_to dashboard_groups_path, title: 'Groups' do
       = icon('group fw')
       %span
         Groups
-  = nav_link(path: 'dashboard#milestones') do
+  = nav_link(controller: :milestones) do
     = link_to dashboard_milestones_path, title: 'Milestones' do
       = icon('clock-o fw')
       %span
@@ -48,6 +48,7 @@
       %span
         Help
 
+  %li.separate-item
   = nav_link(controller: :profile) do
     = link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
       = icon('user fw')
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 0b7de9633ec0849de4faac4c33f5400a375a16b8..55940741dc07e4ca59c67bd384097e37dc0f50bb 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,4 +1,12 @@
 %ul.nav.nav-sidebar
+  = nav_link do
+    = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
+      = icon('caret-square-o-left fw')
+      %span
+        Go to dashboard
+
+  %li.separate-item
+
   = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
     = link_to group_path(@group), title: 'Home' do
       = icon('group fw')
@@ -34,7 +42,7 @@
       %span
         Members
   - if can?(current_user, :admin_group, @group)
-    = nav_link do
+    = nav_link(html_options: { class: "separate-item" }) do
       = link_to edit_group_path(@group), title: 'Settings' do
         = icon ('cogs fw')
         %span
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index cc119fd64e6dd69451e1b58b6114931c523bd49c..3b9d31a6fc5d4f13d67850381ffb71c6ba656f44 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,4 +1,12 @@
 %ul.nav.nav-sidebar
+  = nav_link do
+    = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
+      = icon('caret-square-o-left fw')
+      %span
+        Go to dashboard
+
+  %li.separate-item
+
   = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
     = link_to profile_path, title: 'Profile Settings' do
       = icon('user fw')
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index d0f82b5f57f78caacc24e79b3ddc0617d54c29db..86b46e8c75ec33cf5f4be6463a0c0546f4d98740 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,4 +1,19 @@
 %ul.nav.nav-sidebar
+  - if @project.group
+    = nav_link do
+      = link_to group_path(@project.group), title: 'Go to group', class: 'back-link' do
+        = icon('caret-square-o-left fw')
+        %span
+          Go to group
+  - else
+    = nav_link do
+      = link_to root_path, title: 'Go to dashboard', class: 'back-link' do
+        = icon('caret-square-o-left fw')
+        %span
+          Go to dashboard
+
+  %li.separate-item
+
   = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
     = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
       = icon('bookmark fw')
@@ -98,7 +113,7 @@
           Snippets
 
   - if project_nav_tab? :settings
-    = nav_link(html_options: {class: "#{project_tab_class}"}) do
+    = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
       = link_to edit_project_path(@project), title: 'Settings' do
         = icon('cogs fw')
         %span
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index 4d78215ed3c3bbd19b91da0ecea081437c4e0f1d..b3ed59a1a4aa0a0cc923258a5b7a8e100eff5879 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -1,10 +1,6 @@
 %div
   = form_for [:profile, @key], html: { class: 'js-requires-input' } do |f|
-    - if @key.errors.any?
-      .alert.alert-danger
-        %ul
-          - @key.errors.full_messages.each do |msg|
-            %li= msg
+    = form_errors(@key)
 
     .form-group
       = f.label :key, class: 'label-light'
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 3d15c0d932bc22cda43e17d3fdac39a8bb03ede4..6609295a2a5517fcd4317cfe3b0e7554ccaa63f4 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -2,11 +2,7 @@
 - header_title page_title, profile_notifications_path
 
 = form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f|
-  -if @user.errors.any?
-    %div.alert.alert-danger
-      %ul
-        - @user.errors.full_messages.each do |msg|
-          %li= msg
+  = form_errors(@user)
 
   = hidden_field_tag :notification_type, 'global'
   .row
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 44d758dceb3190e5382b19383abd3d6a97e82eb5..5ac8a8b9d093e9f3340d621384911e4274ca4a38 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -13,11 +13,8 @@
       - unless @user.password_automatically_set?
         or recover your current one
     = form_for @user, url: profile_password_path, method: :put, html: {class: "update-password"} do |f|
-      -if @user.errors.any?
-        .alert.alert-danger
-          %ul
-            - @user.errors.full_messages.each do |msg|
-              %li= msg
+      = form_errors(@user)
+
       - unless @user.password_automatically_set?
         .form-group
           = f.label :current_password, class: 'label-light'
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index d165f758c81f5bc16a09cd81f4d6a05765a80ab4..2eb9fac57c31ae2934f35d3caf178ea3cb078d72 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -7,11 +7,8 @@
     Please set a new password before proceeding.
     %br
     After a successful password update you will be redirected to login screen.
-  -if @user.errors.any?
-    .alert.alert-danger
-      %ul
-        - @user.errors.full_messages.each do |msg|
-          %li= msg
+
+  = form_errors(@user)
 
   - unless @user.password_automatically_set?
     .form-group
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index dcb3be9585db98cc3cc4d2e055636140843aa6a8..f59d27f7ed013307a7a1c0ff4f786529e4255cc9 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,9 +1,6 @@
 = form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit-user prepend-top-default" }, authenticity_token: true do |f|
-  -if @user.errors.any?
-    %div.alert.alert-danger
-      %ul
-        - @user.errors.full_messages.each do |msg|
-          %li= msg
+  = form_errors(@user)
+
   .row
     .col-lg-3.profile-settings-sidebar
       %h4.prepend-top-0
diff --git a/app/views/projects/_errors.html.haml b/app/views/projects/_errors.html.haml
index 7c8bb33ed7edc4f3d868742ec68f1a16005d3aab..2dba22d3be665f0b975f054135b3417aca300ede 100644
--- a/app/views/projects/_errors.html.haml
+++ b/app/views/projects/_errors.html.haml
@@ -1,4 +1 @@
-- if @project.errors.any?
-  .alert.alert-danger
-    %button{ type: "button", class: "close", "data-dismiss" => "alert"} &times;
-    = @project.errors.full_messages.first
+= form_errors(@project)
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 4920910fee11f6c1afd4fadc056d479cde759870..7a78d61a611fabfac4276396fbef43dab47264e4 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -2,13 +2,13 @@
   .md-header
     %ul.nav-links
       %li.active
-        %a.js-md-write-button{ href: "#md-write-holder" }
+        %a.js-md-write-button{ href: "#md-write-holder", tabindex: -1 }
           Write
       %li
-        %a.js-md-preview-button{ href: "#md-preview-holder" }
+        %a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
           Preview
       %li.pull-right
-        %button.zen-cotrol.zen-control-full.js-zen-enter{ type: 'button' }
+        %button.zen-cotrol.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 }
           Go full screen
 
   .md-write-holder
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 7f2903589a90dd501053623795d26d2625676958..7da892312435f4c33da0015d0105d1d9563bd7d9 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -19,24 +19,17 @@
       .pull-right
         - if ci_commit
           = render_ci_status(ci_commit)
-          &nbsp;
         = clipboard_button(clipboard_text: commit.id)
         = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
 
-      .notes_count
-        - if note_count > 0
-          %span.light
-            %i.fa.fa-comments
-            = note_count
-
     - if commit.description?
       .commit-row-description.js-toggle-content
         %pre
           = preserve(markdown(escape_once(commit.description), pipeline: :single_line))
 
     .commit-row-info
+      by
       = commit_author_link(commit, avatar: true, size: 24)
-      authored
       .committed_ago
         #{time_ago_with_tooltip(commit.committed_date, skip_js: true)} &nbsp;
       = link_to_browse_code(project, commit)
diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml
index 5e182af26693f87f24cbefc167a8e3d464b7ba03..f6565f858366b793ace7f3bf50fa14685d9cc7cf 100644
--- a/app/views/projects/deploy_keys/_form.html.haml
+++ b/app/views/projects/deploy_keys/_form.html.haml
@@ -1,10 +1,6 @@
 %div
   = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal js-requires-input' } do |f|
-    -if @key.errors.any?
-      .alert.alert-danger
-        %ul
-          - @key.errors.full_messages.each do |msg|
-            %li= msg
+    = form_errors(@key)
 
     .form-group
       = f.label :title, class: "control-label"
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index 2e1a37aa06d28698482ea489579fbe8348ceefe4..eaab99973a4f1776ed6bb18a19ce669eb25dd9ac 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, diff_refs)
 
-.content-block.oneline-block
+.content-block.oneline-block.files-changed
   .inline-parallel-buttons
     .btn-group
       = inline_diff_btn
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 698ed02ea0e13fdb0111786ecb1b88fd77bbc24c..83a8d7ae9bf54ada02a22ef59cf0bddb4013fcb5 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -3,7 +3,7 @@
     - if diff_file.diff.submodule?
       %span
         = icon('archive fw')
-        %strong
+        %span
           = submodule_link(blob, @commit.id, project.repository)
     - else
       = blob_icon blob.mode, blob.name
@@ -11,13 +11,13 @@
       = link_to "#diff-#{i}" do
         - if diff_file.renamed_file
           - old_path, new_path = mark_inline_diffs(diff_file.old_path, diff_file.new_path)
-          %strong.filename.old
+          .filename.old
             = old_path
           &rarr;
-          %strong.filename.new
+          .filename.new
             = new_path
         - else
-          %strong
+          %span
             = diff_file.new_path
           - if diff_file.deleted_file
             deleted
@@ -28,8 +28,8 @@
 
       .file-actions.hidden-xs
         - if blob_text_viewable?(blob)
-          = link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file" do
-            = icon('comments')
+          = link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip btn-file-option', title: "Toggle comments for this file" do
+            = icon('comment')
           \
 
         - if editable_diff?(diff_file)
diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml
index 67d016bd871015f2c414e03eea5ea413c19a92f6..e39224d86c6a1c88f60c31961ca6492bd40fb7db 100644
--- a/app/views/projects/hooks/index.html.haml
+++ b/app/views/projects/hooks/index.html.haml
@@ -9,10 +9,8 @@
 %hr.clearfix
 
 = form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f|
-  -if @hook.errors.any?
-    .alert.alert-danger
-      - @hook.errors.full_messages.each do |msg|
-        %p= msg
+  = form_errors(@hook)
+
   .form-group
     = f.label :url, "URL", class: 'control-label'
     .col-sm-10
diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml
index be7a0bb5628060f090fe097c4f3a3561e4e94176..aa143e54ffe4a8902be01b18c4579c42f2e059a2 100644
--- a/app/views/projects/labels/_form.html.haml
+++ b/app/views/projects/labels/_form.html.haml
@@ -1,11 +1,5 @@
 = form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form js-quick-submit js-requires-input' } do |f|
-  -if @label.errors.any?
-    .row
-      .col-sm-offset-2.col-sm-10
-        .alert.alert-danger
-          - @label.errors.full_messages.each do |msg|
-            %span= msg
-            %br
+  = form_errors(@label)
 
   .form-group
     = f.label :title, class: 'control-label'
diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml
index 0612863296ae3778a825f69993dcfb1ab313506a..097a65969a65010ee76c532bdf8a2e7a49491395 100644
--- a/app/views/projects/labels/_label.html.haml
+++ b/app/views/projects/labels/_label.html.haml
@@ -1,24 +1,27 @@
 %li{id: dom_id(label)}
   = render "shared/label_row", label: label
 
-  .pull-right
-    %strong.append-right-20
+  .pull-info-right
+    %span.append-right-20
       = link_to_label(label, type: :merge_request) do
-        = pluralize label.open_merge_requests_count, 'open merge request'
+        = pluralize label.open_merge_requests_count, 'merge request'
 
-    %strong.append-right-20
+    %span.append-right-20
       = link_to_label(label) do
         = pluralize label.open_issues_count(current_user), 'open issue'
 
     - if current_user
       .label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}}
         .subscription-status{data: {status: label_subscription_status(label)}}
-        %button.btn.btn-sm.btn-info.subscribe-button
+
+        %a.subscribe-button.btn.action-buttons{data: {toggle: "tooltip"}}
           %span= label_subscription_toggle_button_text(label)
 
     - if can? current_user, :admin_label, @project
-      = link_to 'Edit', edit_namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm'
-      = link_to 'Delete', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
+      = link_to edit_namespace_project_label_path(@project.namespace, @project, label), title: "Edit", class: 'btn action-buttons', data: {toggle: "tooltip"} do
+        %i.fa.fa-pencil-square-o
+      = link_to namespace_project_label_path(@project.namespace, @project, label), title: "Delete", class: 'btn action-buttons remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?", toggle: "tooltip"} do
+        %i.fa.fa-trash-o
 
 - if current_user
   :javascript
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index 01dc7519beea37651610217fbf4d194d02329373..7d7c487e9709daebd056021597f754486e212485 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -5,33 +5,74 @@
   .hide.alert.alert-danger.mr-compare-errors
   .merge-request-branches.row
     .col-md-6
-      .panel.panel-default
+      .panel.panel-default.panel-new-merge-request
         .panel-heading
-          %strong Source branch
-        .panel-body
-          = f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted?, required: true })
-          &nbsp;
-          = f.select(:source_branch, @merge_request.source_branches, { include_blank: true }, { class: 'source_branch select2 span2', required: true, data: { placeholder: "Select source branch" } })
+          Source branch
+        .panel-body.clearfix
+          .merge-request-select.dropdown
+            = f.hidden_field :source_project_id
+            = dropdown_toggle @merge_request.source_project_path, { toggle: "dropdown", field_name: "#{f.object_name}[source_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-source-project" }
+            .dropdown-menu.dropdown-menu-selectable.dropdown-source-project
+              = dropdown_title("Select source project")
+              = dropdown_filter("Search projects")
+              = dropdown_content do
+                - is_active = f.object.source_project_id == @merge_request.source_project.id
+                %ul
+                  %li
+                    %a{ href: "#", class: "#{("is-active" if is_active)}", data: { id: @merge_request.source_project.id } }
+                      = @merge_request.source_project_path
+          .merge-request-select.dropdown
+            = f.hidden_field :source_branch
+            = dropdown_toggle "Select source branch", { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" }
+            .dropdown-menu.dropdown-menu-selectable.dropdown-source-branch
+              = dropdown_title("Select source branch")
+              = dropdown_filter("Search branches")
+              = dropdown_content do
+                %ul
+                  - @merge_request.source_branches.each do |branch|
+                    %li
+                      %a{ href: "#", class: "#{("is-active" if f.object.source_branch == branch)}", data: { id: branch } }
+                        = branch
         .panel-footer
-          .mr_source_commit
+          = icon('spinner spin', class: 'js-source-loading')
+          %ul.list-unstyled.mr_source_commit
 
     .col-md-6
-      .panel.panel-default
+      .panel.panel-default.panel-new-merge-request
         .panel-heading
-          %strong Target branch
-        .panel-body
+          Target branch
+        .panel-body.clearfix
           - projects =  @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project]
-          = f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted?, required: true })
-          &nbsp;
-          = f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', required: true, data: { placeholder: "Select target branch" } })
+          .merge-request-select.dropdown
+            = f.hidden_field :target_project_id
+            = dropdown_toggle f.object.target_project.path_with_namespace, { toggle: "dropdown", field_name: "#{f.object_name}[target_project_id]", disabled: @merge_request.persisted? }, { toggle_class: "js-compare-dropdown js-target-project" }
+            .dropdown-menu.dropdown-menu-selectable.dropdown-target-project
+              = dropdown_title("Select target project")
+              = dropdown_filter("Search projects")
+              = dropdown_content do
+                %ul
+                  - projects.each do |project|
+                    %li
+                      %a{ href: "#", class: "#{("is-active" if f.object.target_project_id == project.id)}", data: { id: project.id } }
+                        = project.path_with_namespace
+          .merge-request-select.dropdown
+            = f.hidden_field :target_branch
+            = dropdown_toggle f.object.target_branch, { toggle: "dropdown", field_name: "#{f.object_name}[target_branch]" }, { toggle_class: "js-compare-dropdown js-target-branch" }
+            .dropdown-menu.dropdown-menu-selectable.dropdown-target-branch.js-target-branch-dropdown
+              = dropdown_title("Select target branch")
+              = dropdown_filter("Search branches")
+              = dropdown_content do
+                %ul
+                  - @merge_request.target_branches.each do |branch|
+                    %li
+                      %a{ href: "#", class: "#{("is-active" if f.object.target_branch == branch)}", data: { id: branch } }
+                        = branch
         .panel-footer
-          .mr_target_commit
+          = icon('spinner spin', class: "js-target-loading")
+          %ul.list-unstyled.mr_target_commit
 
   - if @merge_request.errors.any?
-    .alert.alert-danger
-      - @merge_request.errors.full_messages.each do |msg|
-        %div= msg
-
+    = form_errors(@merge_request)
   - elsif @merge_request.source_branch.present? && @merge_request.target_branch.present?
     .light-well.append-bottom-default
       .center
@@ -45,40 +86,11 @@
             and
             %span.label-branch #{@merge_request.target_branch}
             are the same.
-
-
-  .form-actions
-    = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn"
-
-:javascript
-  var source_branch = $("#merge_request_source_branch")
-    , target_branch = $("#merge_request_target_branch")
-    , target_project = $("#merge_request_target_project_id");
-
-  $.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: source_branch.val() });
-  $.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
-
-  target_project.on("change", function() {
-    $.get("#{update_branches_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id:  $(this).val() });
-  });
-  source_branch.on("change", function() {
-    $.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: $(this).val() });
-    $(".mr-compare-errors").fadeOut();
-    $(".mr-compare-btn").enable();
-  });
-  target_branch.on("change", function() {
-    $.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
-    $(".mr-compare-errors").fadeOut();
-    $(".mr-compare-btn").enable();
-  });
-
+  = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn"
 
 :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;
-    }
+  new Compare({
+    targetProjectUrl: "#{update_branches_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}",
+    sourceBranchUrl: "#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}",
+    targetBranchUrl: "#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}"
   });
diff --git a/app/views/projects/merge_requests/branch_from.html.haml b/app/views/projects/merge_requests/branch_from.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..4f90dde6fa801e2f065ac6959f7705e6b4818a2a
--- /dev/null
+++ b/app/views/projects/merge_requests/branch_from.html.haml
@@ -0,0 +1 @@
+= commit_to_html(@commit, @source_project, false)
diff --git a/app/views/projects/merge_requests/branch_from.js.haml b/app/views/projects/merge_requests/branch_from.js.haml
deleted file mode 100644
index 9210798f39c051f4fff1f98910bf68f3147e817d..0000000000000000000000000000000000000000
--- a/app/views/projects/merge_requests/branch_from.js.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-:plain
-  $(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}");
-  $('.js-timeago').timeago()
diff --git a/app/views/projects/merge_requests/branch_to.html.haml b/app/views/projects/merge_requests/branch_to.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..67a7a6bcec9d993f28f9f2c578797cd95100aaeb
--- /dev/null
+++ b/app/views/projects/merge_requests/branch_to.html.haml
@@ -0,0 +1 @@
+= commit_to_html(@commit, @target_project, false)
diff --git a/app/views/projects/merge_requests/branch_to.js.haml b/app/views/projects/merge_requests/branch_to.js.haml
deleted file mode 100644
index 32fe2d535f319ff7bac1077fcf7fbd22c85bace4..0000000000000000000000000000000000000000
--- a/app/views/projects/merge_requests/branch_to.js.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-:plain
-  $(".mr_target_commit").html("#{commit_to_html(@commit, @target_project, false)}");
-  $('.js-timeago').timeago()
diff --git a/app/views/projects/merge_requests/update_branches.html.haml b/app/views/projects/merge_requests/update_branches.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..1b93188a10c70a2683224bac1e0e4dcd8d14cec6
--- /dev/null
+++ b/app/views/projects/merge_requests/update_branches.html.haml
@@ -0,0 +1,5 @@
+%ul
+  - @target_branches.each do |branch|
+    %li
+      %a{ href: "#", class: "#{("is-active" if "a" == branch)}", data: { id: branch } }
+        = branch
diff --git a/app/views/projects/merge_requests/update_branches.js.haml b/app/views/projects/merge_requests/update_branches.js.haml
deleted file mode 100644
index ca21b3bc0deb365c4db2f3ecc72576c80cd5e4e4..0000000000000000000000000000000000000000
--- a/app/views/projects/merge_requests/update_branches.js.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-:plain
-  $(".target_branch").html("#{escape_javascript(options_for_select(@target_branches))}");
-
-  $('select.target_branch').select2({
-    width: 'resolve',
-    dropdownAutoWidth: true
-  });
-
-  $(".mr_target_commit").html("");
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 23f2bca7baf470f0f34926bd117342e10036983b..b2dae1c70ee0acb614d9dca54b3808a7df5a8f17 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -1,9 +1,6 @@
 = form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-quick-submit js-requires-input'}  do |f|
-  -if @milestone.errors.any?
-    .alert.alert-danger
-      %ul
-        - @milestone.errors.full_messages.each do |msg|
-          %li= msg
+  = form_errors(@milestone)
+
   .row
     .col-md-6
       .form-group
diff --git a/app/views/projects/notes/_diff_notes_with_reply.html.haml b/app/views/projects/notes/_diff_notes_with_reply.html.haml
index 11f9859a90f4e7c4004d9cd77646784ab9f49d48..39be072855ae014157db8f6ab1c038e2e2782088 100644
--- a/app/views/projects/notes/_diff_notes_with_reply.html.haml
+++ b/app/views/projects/notes/_diff_notes_with_reply.html.haml
@@ -3,9 +3,6 @@
 - if !defined?(line) || line == note.diff_line
   %tr.notes_holder
     %td.notes_line{ colspan: 2 }
-      %span.discussion-notes-count
-        %i.fa.fa-comment
-        = notes.count
     %td.notes_content
       %ul.notes{ data: { discussion_id: note.discussion_id } }
         = render notes
diff --git a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml
index bb761ed2f946b0dd21438077b86253a1ab8ddbaa..f8aa5e2fa7dfcf830194479e2d3d69cc7230f41e 100644
--- a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml
+++ b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml
@@ -4,9 +4,6 @@
 %tr.notes_holder
   - if note1
     %td.notes_line.old
-      %span.btn.disabled
-        %i.fa.fa-comment
-        = notes_left.count
     %td.notes_content.parallel.old
       %ul.notes{ data: { discussion_id: note1.discussion_id } }
         = render notes_left
@@ -19,9 +16,6 @@
 
   - if note2
     %td.notes_line.new
-      %span.btn.disabled
-        %i.fa.fa-comment
-        = notes_right.count
     %td.notes_content.parallel.new
       %ul.notes{ data: { discussion_id: note2.discussion_id } }
         = render notes_right
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index a681d6dece46785c46f1054e2a8691bb0713c33a..5c42423541e6021f9b39667a41cf20db8433b4fc 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -17,8 +17,8 @@
               %span.note-role
                 = access
             = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do
-              = icon('pencil-square-o')
-            = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete' do
+              = icon('pencil')
+            = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button js-note-delete danger' do
               = icon('trash-o')
       .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''}
         .note-text
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index cfd7e1534ca81c1d24778fa72787f9a3b7353e99..653b02da4dbbb10d1bf10c952dd3ac479326f555 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -13,11 +13,7 @@
 
 - if can? current_user, :admin_project, @project
   = form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'form-horizontal' } do |f|
-    -if @protected_branch.errors.any?
-      .alert.alert-danger
-        %ul
-          - @protected_branch.errors.full_messages.each do |msg|
-            %li= msg
+    = form_errors(@protected_branch)
 
     .form-group
       = f.label :name, "Branch", class: 'control-label'
diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml
index efe1e6f24c22e144d7b32de64321dfbd9d1fe1c3..ca284b84d39150deb4f6933655129154649bb73b 100644
--- a/app/views/projects/variables/show.html.haml
+++ b/app/views/projects/variables/show.html.haml
@@ -13,13 +13,7 @@
 
 
 = nested_form_for @project, url: url_for(controller: 'projects/variables', action: 'update'), html: { class: 'form-horizontal' }  do |f|
-  - if @project.errors.any?
-    #error_explanation
-      %p.lead= "#{pluralize(@project.errors.count, "error")} prohibited this project from being saved:"
-      .alert.alert-error
-        %ul
-          - @project.errors.full_messages.each do |msg|
-            %li= msg
+  = form_errors(@project)
 
   = f.fields_for :variables do |variable_form|
     .form-group
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index f0d1932e23cfb1514ce9aeff32905a5f9998a81b..812876e283562f30afff796211508b2a78871362 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,9 +1,5 @@
 = form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default js-quick-submit' } do |f|
-  -if @page.errors.any?
-    #error_explanation
-      .alert.alert-danger
-        - @page.errors.full_messages.each do |msg|
-          %p= msg
+  = form_errors(@page)
 
   = f.hidden_field :title, value: @page.title
   .form-group
diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml
index 4b47b0291be69a334ad44bdbd2f1a19d7f25415a..b38c5e18efba387eee30a26316c1c6a006c698a6 100644
--- a/app/views/shared/_label_row.html.haml
+++ b/app/views/shared/_label_row.html.haml
@@ -1,4 +1,5 @@
 %span.label-row
-  = link_to_label(label, tooltip: false)
+  %span.label-name
+    = link_to_label(label, tooltip: false)
   %span.prepend-left-10
     = markdown(label.description, pipeline: :single_line)
diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml
index 5a60ff5a5da8c9fa8e598436610d1fc21208fde9..fc935166bf67379cb59f8de94df0ed00776e753f 100644
--- a/app/views/shared/_service_settings.html.haml
+++ b/app/views/shared/_service_settings.html.haml
@@ -1,9 +1,4 @@
-- if @service.errors.any?
-  #error_explanation
-    .alert.alert-danger
-      %ul
-        - @service.errors.full_messages.each do |msg|
-          %li= msg
+= form_errors(@service)
 
 - if @service.help.present?
   .well
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index e2a9e5bfb9234574d197a76468a181f992be1a2a..757a3812deb94588a1aef40237486d8c5f2cef02 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -1,10 +1,5 @@
-- if issuable.errors.any?
-  .row
-    .col-sm-offset-2.col-sm-10
-      .alert.alert-danger
-        - issuable.errors.full_messages.each do |msg|
-          %span= msg
-          %br
+= form_errors(issuable)
+
 .form-group
   = f.label :title, class: 'control-label'
   .col-sm-10
@@ -53,10 +48,11 @@
     .issue-assignee
       = f.label :assignee_id, "Assignee", class: 'control-label'
       .col-sm-10
-        = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
-            placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
-            selected: issuable.assignee_id, project: @target_project || @project,
-            first_user: true, current_user: true, include_blank: true)
+        .issuable-form-select-holder
+          = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
+              placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
+              selected: issuable.assignee_id, project: @target_project || @project,
+              first_user: true, current_user: true, include_blank: true)
         &nbsp;
         = link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
   .form-group
@@ -64,8 +60,9 @@
       = f.label :milestone_id, "Milestone", class: 'control-label'
       .col-sm-10
         - if milestone_options(issuable).present?
-          = f.select(:milestone_id, milestone_options(issuable),
-            { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
+          .issuable-form-select-holder
+            = f.select(:milestone_id, milestone_options(issuable),
+              { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
         - else
           .prepend-top-10
           %span.light No open milestones available.
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 1041eccd1dfb0a08b160b2560535d0d348a20a85..47ec09f62c604262456d2267470f7f916f8861a8 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -1,10 +1,6 @@
 .snippet-form-holder
   = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input" } do |f|
-    - if @snippet.errors.any?
-      .alert.alert-danger
-        %ul
-          - @snippet.errors.full_messages.each do |msg|
-            %li= msg
+    = form_errors(@snippet)
 
     .form-group
       = f.label :title, class: 'control-label'
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
index 7f29918dba371da38fb3950388f9fed69d0d7ae2..1de71f37d1a9dd5bb4debcd3d500587b81d98d47 100644
--- a/app/views/users/calendar.html.haml
+++ b/app/views/users/calendar.html.haml
@@ -7,4 +7,4 @@
       '#{user_calendar_activities_path}'
     );
 
-.calendar-hint Summary of issues, merge requests and push events
+.calendar-hint Summary of issues, merge requests, and push events
diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml
index 8ffcdc4a327e0a1dc5ea9bfdf526e1d9e9ac7e0b..dc249155b92a393a6546e0591621fc14727b7292 100644
--- a/app/views/votes/_votes_block.html.haml
+++ b/app/views/votes/_votes_block.html.haml
@@ -1,7 +1,7 @@
 .awards.votes-block
   - awards_sort(votable.notes.awards.grouped_awards).each do |emoji, notes|
-    %button.btn.award-control.js-emoji-btn.has-tooltip{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user), data: {placement: "top"}}
-      = emoji_icon(emoji)
+    %button.btn.award-control.js-emoji-btn.has-tooltip{class: (note_active_class(notes, current_user)), data: {placement: "top", original_title: emoji_author_list(notes, current_user)}}
+      = emoji_icon(emoji, sprite: false)
       %span.award-control-text.js-counter
         = notes.count
 
diff --git a/config/application.rb b/config/application.rb
index 5a0ac70aa2aabcd46252f149ba0098d4726920ef..2e2ed48db07432d1c5c4901b4d4fc1f41fecb661 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -4,11 +4,9 @@ require 'rails/all'
 require 'devise'
 I18n.config.enforce_available_locales = false
 Bundler.require(:default, Rails.env)
-require_relative '../lib/gitlab/redis_config'
+require_relative '../lib/gitlab/redis'
 
 module Gitlab
-  REDIS_CACHE_NAMESPACE = 'cache:gitlab'
-
   class Application < Rails::Application
     # Settings in config/environments/* take precedence over those specified here.
     # Application configuration should go into files in config/initializers
@@ -69,8 +67,8 @@ module Gitlab
       end
     end
 
-    redis_config_hash = Gitlab::RedisConfig.redis_store_options
-    redis_config_hash[:namespace] = REDIS_CACHE_NAMESPACE
+    redis_config_hash = Gitlab::Redis.redis_store_options
+    redis_config_hash[:namespace] = Gitlab::Redis::CACHE_NAMESPACE
     redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
     config.cache_store = :redis_store, redis_config_hash
 
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 909526605a194da423811eba359549c1e1de5391..a9d8ac4b6d47f2dd1e11e774ec2c9ecbbf7e72b6 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -21,6 +21,9 @@ Rails.application.configure do
   # Generate digests for assets URLs
   config.assets.digest = true
 
+  # Enable compression of compiled assets using gzip.
+  config.assets.compress = true
+
   # Defaults to nil and saved in location specified by config.assets.prefix
   # config.assets.manifest = YOUR_PATH
 
diff --git a/config/environments/test.rb b/config/environments/test.rb
index f96ac6f97530378c206d2cebcc0842f29c797113..a703c0934f7ffe3f4c4e6c1a218d955785e0261b 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -8,6 +8,7 @@ Rails.application.configure do
   config.cache_classes = false
 
   # Configure static asset server for tests with Cache-Control for performance
+  config.assets.digest = false
   config.serve_static_files = true
   config.static_cache_control = "public, max-age=3600"
 
diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb
index 3e1deb8d30645d5ed3e48640023e3ed0b2f41020..1b445bbbd10200baee8b5b0485206bd480c8a8fd 100644
--- a/config/initializers/metrics.rb
+++ b/config/initializers/metrics.rb
@@ -7,6 +7,7 @@ if Gitlab::Metrics.enabled?
   # ActiveSupport.
   require 'gitlab/metrics/subscribers/action_view'
   require 'gitlab/metrics/subscribers/active_record'
+  require 'gitlab/metrics/subscribers/rails_cache'
 
   Gitlab::Application.configure do |config|
     config.middleware.use(Gitlab::Metrics::RackMiddleware)
@@ -74,6 +75,29 @@ if Gitlab::Metrics.enabled?
       config.instrument_methods(const)
       config.instrument_instance_methods(const)
     end
+
+    # Instruments all Banzai filters
+    Dir[Rails.root.join('lib', 'banzai', 'filter', '*.rb')].each do |file|
+      klass = File.basename(file, File.extname(file)).camelize
+      const = Banzai::Filter.const_get(klass)
+
+      config.instrument_methods(const)
+      config.instrument_instance_methods(const)
+    end
+
+    config.instrument_methods(Banzai::ReferenceExtractor)
+    config.instrument_instance_methods(Banzai::ReferenceExtractor)
+
+    config.instrument_methods(Banzai::Renderer)
+    config.instrument_methods(Banzai::Querying)
+
+    [Issuable, Mentionable, Participable].each do |klass|
+      config.instrument_instance_methods(klass)
+      config.instrument_instance_methods(klass::ClassMethods)
+    end
+
+    config.instrument_methods(Gitlab::ReferenceExtractor)
+    config.instrument_instance_methods(Gitlab::ReferenceExtractor)
   end
 
   GC::Profiler.enable
diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
index 3da5d46be92a4796d0ad1b9e5dc415dfdb81bf57..70285255877b6399bae7b52259023d6ece08aca3 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -13,7 +13,7 @@ end
 if Rails.env.test?
   Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session"
 else
-  redis_config = Gitlab::RedisConfig.redis_store_options
+  redis_config = Gitlab::Redis.redis_store_options
   redis_config[:namespace] = 'session:gitlab'
   
   Gitlab::Application.config.session_store(
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index cc83137745ad220d398fa02b498d27245827b1b2..9182d9298097dca390dda7d8e4240afb498fa23a 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -2,7 +2,7 @@ SIDEKIQ_REDIS_NAMESPACE = 'resque:gitlab'
 
 Sidekiq.configure_server do |config|
   config.redis = {
-    url: Gitlab::RedisConfig.url,
+    url: Gitlab::Redis.url,
     namespace: SIDEKIQ_REDIS_NAMESPACE
   }
 
@@ -29,7 +29,7 @@ end
 
 Sidekiq.configure_client do |config|
   config.redis = {
-    url: Gitlab::RedisConfig.url,
+    url: Gitlab::Redis.url,
     namespace: SIDEKIQ_REDIS_NAMESPACE
   }
 end
diff --git a/config/mail_room.yml b/config/mail_room.yml
index 60257329f3ebf7b2128ffdb64c3fb0f36cfd53ff..761a32adb9ee4f14ace3031b0349182bcd1bf16d 100644
--- a/config/mail_room.yml
+++ b/config/mail_room.yml
@@ -2,7 +2,7 @@
 <%
 require "yaml"
 require "json"
-require_relative "lib/gitlab/redis_config"
+require_relative "lib/gitlab/redis"
 
 rails_env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
 
@@ -18,7 +18,7 @@ if File.exists?(config_file)
   config['mailbox']    = "inbox"  if config['mailbox'].nil?
 
   if config['enabled'] && config['address']
-    redis_url = Gitlab::RedisConfig.new(rails_env).url
+    redis_url = Gitlab::Redis.new(rails_env).url
     %>
     -
       :host: <%= config['host'].to_json %>
diff --git a/db/fixtures/development/07_milestones.rb b/db/fixtures/development/07_milestones.rb
index e028ac82ba33898e1f0185fd47845ec764e72fe9..540e4e6825947dc2c04a67ff513ac365f6a8878f 100644
--- a/db/fixtures/development/07_milestones.rb
+++ b/db/fixtures/development/07_milestones.rb
@@ -4,7 +4,7 @@ Gitlab::Seeder.quiet do
       milestone_params = {
         title: "v#{i}.0",
         description: FFaker::Lorem.sentence,
-        state: ['opened', 'closed'].sample,
+        state: [:active, :closed].sample,
       }
 
       milestone = Milestones::CreateService.new(
diff --git a/db/migrate/20130315124931_user_color_scheme.rb b/db/migrate/20130315124931_user_color_scheme.rb
index fe139e32ea72301461edc2d938ad4d65fc7572ed..56c9a31ee3ca96984c74804a4fd25f4192df075e 100644
--- a/db/migrate/20130315124931_user_color_scheme.rb
+++ b/db/migrate/20130315124931_user_color_scheme.rb
@@ -1,7 +1,9 @@
 class UserColorScheme < ActiveRecord::Migration
+  include Gitlab::Database
+
   def up
     add_column :users, :color_scheme_id, :integer, null: false, default: 1
-    User.where(dark_scheme: true).update_all(color_scheme_id: 2)
+    execute("UPDATE users SET color_scheme_id = 2 WHERE dark_scheme = #{true_value}")
     remove_column :users, :dark_scheme
   end
 
diff --git a/db/migrate/20130403003950_add_last_activity_column_into_project.rb b/db/migrate/20130403003950_add_last_activity_column_into_project.rb
index 2a036bd99938652ad8ef6fbad45c6e3738da4842..85e31608d791fe7b7a6517605eccd51bdfc419a0 100644
--- a/db/migrate/20130403003950_add_last_activity_column_into_project.rb
+++ b/db/migrate/20130403003950_add_last_activity_column_into_project.rb
@@ -3,14 +3,16 @@ class AddLastActivityColumnIntoProject < ActiveRecord::Migration
     add_column :projects, :last_activity_at, :datetime
     add_index :projects, :last_activity_at
 
-    Project.find_each do |project|
-      last_activity_date = if project.last_activity
-                             project.last_activity.created_at
-                           else
-                             project.updated_at
-                           end
+    select_all('SELECT id, updated_at FROM projects').each do |project|
+      project_id = project['id']
+      update_date = project['updated_at']
+      event = select_one("SELECT created_at FROM events WHERE project_id = #{project_id} ORDER BY created_at DESC LIMIT 1")
 
-      project.update_attribute(:last_activity_at, last_activity_date)
+      if event && event['created_at']
+        update_date = event['created_at']
+      end
+
+      execute("UPDATE projects SET last_activity_at = '#{update_date}' WHERE id = #{project_id}")
     end
   end
 
diff --git a/db/migrate/20131112220935_add_visibility_level_to_projects.rb b/db/migrate/20131112220935_add_visibility_level_to_projects.rb
index cf1e9f912a04957ee92486d189ae1edf74ddd83f..89421cbedaddc00641710b50cdcfb7e9bc9314cd 100644
--- a/db/migrate/20131112220935_add_visibility_level_to_projects.rb
+++ b/db/migrate/20131112220935_add_visibility_level_to_projects.rb
@@ -1,13 +1,15 @@
 class AddVisibilityLevelToProjects < ActiveRecord::Migration
+  include Gitlab::Database
+
   def self.up
     add_column :projects, :visibility_level, :integer, :default => 0, :null => false
-    Project.where(public: true).update_all(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+    execute("UPDATE projects SET visibility_level = #{Gitlab::VisibilityLevel::PUBLIC} WHERE public = #{true_value}")
     remove_column :projects, :public
   end
 
   def self.down
     add_column :projects, :public, :boolean, :default => false, :null => false
-    Project.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).update_all(public: true)
+    execute("UPDATE projects SET public = #{true_value} WHERE visibility_level = #{Gitlab::VisibilityLevel::PUBLIC}")
     remove_column :projects, :visibility_level
   end
 end
diff --git a/db/migrate/20140313092127_migrate_already_imported_projects.rb b/db/migrate/20140313092127_migrate_already_imported_projects.rb
index f4392c0f05e8db595c9cd3524623d109b2c3dd52..0a9f73a5758f7f647dee254264cb0c0f4e539f7a 100644
--- a/db/migrate/20140313092127_migrate_already_imported_projects.rb
+++ b/db/migrate/20140313092127_migrate_already_imported_projects.rb
@@ -1,12 +1,14 @@
 class MigrateAlreadyImportedProjects < ActiveRecord::Migration
+  include Gitlab::Database
+
   def up
-    Project.where(imported: true).update_all(import_status: "finished")
-    Project.where(imported: false).update_all(import_status: "none")
+    execute("UPDATE projects SET import_status = 'finished' WHERE imported = #{true_value}")
+    execute("UPDATE projects SET import_status = 'none' WHERE imported = #{false_value}")
     remove_column :projects, :imported
   end
 
   def down
     add_column :projects, :imported, :boolean, default: false
-    Project.where(import_status: 'finished').update_all(imported: true)
+    execute("UPDATE projects SET imported = #{true_value} WHERE import_status = 'finished'")
   end
 end
diff --git a/db/migrate/20141007100818_add_visibility_level_to_snippet.rb b/db/migrate/20141007100818_add_visibility_level_to_snippet.rb
index 7f125acb5d1d2803e15ab6f88533796373749518..93826185e8b1d8b4951c421932219a4175349169 100644
--- a/db/migrate/20141007100818_add_visibility_level_to_snippet.rb
+++ b/db/migrate/20141007100818_add_visibility_level_to_snippet.rb
@@ -1,9 +1,11 @@
 class AddVisibilityLevelToSnippet < ActiveRecord::Migration
+  include Gitlab::Database
+
   def up
     add_column :snippets, :visibility_level, :integer, :default => 0, :null => false
 
-    Snippet.where(private: true).update_all(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
-    Snippet.where(private: false).update_all(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+    execute("UPDATE snippets SET visibility_level = #{Gitlab::VisibilityLevel::PRIVATE} WHERE private = #{true_value}")
+    execute("UPDATE snippets SET visibility_level = #{Gitlab::VisibilityLevel::INTERNAL} WHERE private = #{false_value}")
 
     add_index :snippets, :visibility_level
 
@@ -12,10 +14,10 @@ class AddVisibilityLevelToSnippet < ActiveRecord::Migration
 
   def down
     add_column :snippets, :private, :boolean, :default => false, :null => false
-    
-    Snippet.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).update_all(private: false)
-    Snippet.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).update_all(private: true)
-    
+
+    execute("UPDATE snippets SET private = #{false_value} WHERE visibility_level = #{Gitlab::VisibilityLevel::INTERNAL}")
+    execute("UPDATE snippets SET private = #{true_value} WHERE visibility_level = #{Gitlab::VisibilityLevel::PRIVATE}")
+
     remove_column :snippets, :visibility_level
   end
 end
diff --git a/db/schema.rb b/db/schema.rb
index e63e22ce864e30e5b8302cef64e65fe0fec0d7a9..ec235c19131645d54a26a40d451599b2ace8130a 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: 20160331133914) do
+ActiveRecord::Schema.define(version: 20160331223143) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -44,7 +44,6 @@ ActiveRecord::Schema.define(version: 20160331133914) do
     t.datetime "updated_at"
     t.string   "home_page_url"
     t.integer  "default_branch_protection",         default: 2
-    t.boolean  "twitter_sharing_enabled",           default: true
     t.text     "restricted_visibility_levels"
     t.boolean  "version_check_enabled",             default: true
     t.integer  "max_attachment_size",               default: 10,          null: false
diff --git a/doc/api/issues.md b/doc/api/issues.md
index cc6355d34ef851667413afddee0a7a59511e422f..1c635a6cdcf82f49f587950a3a820eafce5117a9 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -76,8 +76,9 @@ Example response:
       "title" : "Consequatur vero maxime deserunt laboriosam est voluptas dolorem.",
       "created_at" : "2016-01-04T15:31:51.081Z",
       "iid" : 6,
-      "labels" : []
-   },
+      "labels" : [],
+      "subscribed" : false
+   }
 ]
 ```
 
@@ -152,7 +153,8 @@ Example response:
       "id" : 41,
       "title" : "Ut commodi ullam eos dolores perferendis nihil sunt.",
       "updated_at" : "2016-01-04T15:31:46.176Z",
-      "created_at" : "2016-01-04T15:31:46.176Z"
+      "created_at" : "2016-01-04T15:31:46.176Z",
+      "subscribed" : false
    }
 ]
 ```
@@ -213,7 +215,8 @@ Example response:
    "id" : 41,
    "title" : "Ut commodi ullam eos dolores perferendis nihil sunt.",
    "updated_at" : "2016-01-04T15:31:46.176Z",
-   "created_at" : "2016-01-04T15:31:46.176Z"
+   "created_at" : "2016-01-04T15:31:46.176Z",
+   "subscribed": false
 }
 ```
 
@@ -267,7 +270,8 @@ Example response:
    },
    "description" : null,
    "updated_at" : "2016-01-07T12:44:33.959Z",
-   "milestone" : null
+   "milestone" : null,
+   "subscribed" : true
 }
 ```
 
@@ -323,7 +327,8 @@ Example response:
    ],
    "id" : 85,
    "assignee" : null,
-   "milestone" : null
+   "milestone" : null,
+   "subscribed" : true
 }
 ```
 
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index b20a6300b7ad9aefe0a8bedcf927dfd3d1a900da..20db73ea6c0b13a03610acda10be396bf753443b 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -66,7 +66,8 @@ Parameters:
       "due_date": null
     },
     "merge_when_build_succeeds": true,
-    "merge_status": "can_be_merged"
+    "merge_status": "can_be_merged",
+    "subscribed" : false
   }
 ]
 ```
@@ -128,7 +129,8 @@ Parameters:
     "due_date": null
   },
   "merge_when_build_succeeds": true,
-  "merge_status": "can_be_merged"
+  "merge_status": "can_be_merged",
+  "subscribed" : true
 }
 ```
 
@@ -227,6 +229,7 @@ Parameters:
   },
   "merge_when_build_succeeds": true,
   "merge_status": "can_be_merged",
+  "subscribed" : true,
   "changes": [
     {
     "old_path": "VERSION",
@@ -304,7 +307,8 @@ Parameters:
     "due_date": null
   },
   "merge_when_build_succeeds": true,
-  "merge_status": "can_be_merged"
+  "merge_status": "can_be_merged",
+  "subscribed" : true
 }
 ```
 
@@ -373,7 +377,8 @@ Parameters:
     "due_date": null
   },
   "merge_when_build_succeeds": true,
-  "merge_status": "can_be_merged"
+  "merge_status": "can_be_merged",
+  "subscribed" : true
 }
 ```
 
@@ -466,7 +471,8 @@ Parameters:
     "due_date": null
   },
   "merge_when_build_succeeds": true,
-  "merge_status": "can_be_merged"
+  "merge_status": "can_be_merged",
+  "subscribed" : true
 }
 ```
 
@@ -530,7 +536,8 @@ Parameters:
     "due_date": null
   },
   "merge_when_build_succeeds": true,
-  "merge_status": "can_be_merged"
+  "merge_status": "can_be_merged",
+  "subscribed" : true
 }
 ```
 
diff --git a/doc/ci/build_artifacts/README.md b/doc/ci/build_artifacts/README.md
index 71db5aa5dc86f9cc28c93e8975c34538be38b24d..9553bb11e9d759b60f3017ba5a48874cf31e5029 100644
--- a/doc/ci/build_artifacts/README.md
+++ b/doc/ci/build_artifacts/README.md
@@ -1,7 +1,10 @@
 # Introduction to build artifacts
 
 Artifacts is a list of files and directories which are attached to a build
-after it completes successfully.
+after it completes successfully.  This feature is enabled by default in all GitLab installations.
+
+_If you are searching for ways to use artifacts, jump to
+[Defining artifacts in `.gitlab-ci.yml`](#defining-artifacts-in-gitlab-ciyml)._
 
 Since GitLab 8.2 and [GitLab Runner] 0.7.0, build artifacts that are created by
 GitLab Runner are uploaded to GitLab and are downloadable as a single archive
@@ -16,13 +19,9 @@ The artifacts browser will be available only for new artifacts that are sent
 to GitLab using GitLab Runner version 1.0 and up. It will not be possible to
 browse old artifacts already uploaded to GitLab.
 
-## Enabling build artifacts
-
-_If you are searching for ways to use artifacts, jump to
-[Defining artifacts in `.gitlab-ci.yml`](#defining-artifacts-in-gitlab-ciyml)._
+## Disabling build artifacts
 
-The artifacts feature is enabled by default in all GitLab installations.
-To disable it site-wide, follow the steps below.
+To disable artifacts site-wide, follow the steps below.
 
 ---
 
diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md
index 210f9c3e849a199da62f2af285162448c5adfec6..d790015aca1a0c0fd0f901bf4f863260031a27a9 100644
--- a/doc/ci/ssh_keys/README.md
+++ b/doc/ci/ssh_keys/README.md
@@ -57,7 +57,7 @@ before_script:
   # WARNING: Use this only with the Docker executor, if you use it with shell
   # you will overwrite your user's SSH config.
   - mkdir -p ~/.ssh
-  - '[[ -f /.dockerinit ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config`
+  - '[[ -f /.dockerinit ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
 ```
 
 As a final step, add the _public_ key from the one you created earlier to the
diff --git a/doc/development/README.md b/doc/development/README.md
index 8940b558fb660de948cff72abafd3babf12b3584..3f3ef068f960d543ebb6b65093a32685a67a8a1a 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -2,6 +2,8 @@
 
 - [Architecture](architecture.md) of GitLab
 - [CI setup](ci_setup.md) for testing GitLab
+- [Code review guidelines](code_review.md) for reviewing code and having code
+  reviewed.
 - [Gotchas](gotchas.md) to avoid
 - [How to dump production data to staging](db_dump.md)
 - [Instrumentation](instrumentation.md)
@@ -10,4 +12,5 @@
 - [Shell commands](shell_commands.md) in the GitLab codebase
 - [Sidekiq debugging](sidekiq_debugging.md)
 - [SQL guidelines](sql.md) for SQL guidelines
+- [Testing standards and style guidelines](testing.md)
 - [UI guide](ui_guide.md) for building GitLab with existing css styles and elements
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
new file mode 100644
index 0000000000000000000000000000000000000000..40ae55ab905527733b6c962d3db4b8a9f2b3d269
--- /dev/null
+++ b/doc/development/code_review.md
@@ -0,0 +1,78 @@
+# Code Review Guidelines
+
+This guide contains advice and best practices for performing code review, and
+having your code reviewed.
+
+All merge requests for GitLab CE and EE, whether written by a GitLab team member
+or a volunteer contributor, must go through a code review process to ensure the
+code is effective, understandable, and maintainable.
+
+Any developer can, and is encouraged to, perform code review on merge requests
+of colleagues and contributors. However, the final decision to accept a merge
+request is up to one of our merge request "endbosses", denoted on the
+[team page](https://about.gitlab.com/team).
+
+## Everyone
+
+- Accept that many programming decisions are opinions. Discuss tradeoffs, which
+  you prefer, and reach a resolution quickly.
+- Ask questions; don't make demands. ("What do you think about naming this
+  `:user_id`?")
+- Ask for clarification. ("I didn't understand. Can you clarify?")
+- Avoid selective ownership of code. ("mine", "not mine", "yours")
+- Avoid using terms that could be seen as referring to personal traits. ("dumb",
+  "stupid"). Assume everyone is attractive, intelligent, and well-meaning.
+- Be explicit. Remember people don't always understand your intentions online.
+- Be humble. ("I'm not sure - let's look it up.")
+- Don't use hyperbole. ("always", "never", "endlessly", "nothing")
+- Be careful about the use of sarcasm. Everything we do is public; what seems
+  like good-natured ribbing to you and a long-time colleague might come off as
+  mean and unwelcoming to a person new to the project.
+- Consider one-on-one chats or video calls if there are too many "I didn't
+  understand" or "Alternative solution:" comments. Post a follow-up comment
+  summarizing one-on-one discussion.
+
+## Having your code reviewed
+
+- The first reviewer of your code is _you_. Before you perform that first push
+  of your shiny new branch, read through the entire diff. Does it make sense?
+  Did you include something unrelated to the overall purpose of the changes? Did
+  you forget to remove any debugging code?
+- Be grateful for the reviewer's suggestions. ("Good call. I'll make that
+  change.")
+- Don't take it personally. The review is of the code, not of you.
+- Explain why the code exists. ("It's like that because of these reasons. Would
+  it be more clear if I rename this class/file/method/variable?")
+- Extract unrelated changes and refactorings into future merge requests/issues.
+- Seek to understand the reviewer's perspective.
+- Try to respond to every comment.
+- Push commits based on earlier rounds of feedback as isolated commits to the
+  branch. Do not squash until the branch is ready to merge. Reviewers should be
+  able to read individual updates based on their earlier feedback.
+
+## Reviewing code
+
+Understand why the change is necessary (fixes a bug, improves the user
+experience, refactors the existing code). Then:
+
+- Communicate which ideas you feel strongly about and those you don't.
+- Identify ways to simplify the code while still solving the problem.
+- Offer alternative implementations, but assume the author already considered
+  them. ("What do you think about using a custom validator here?")
+- Seek to understand the author's perspective.
+- If you don't understand a piece of code, _say so_. There's a good chance
+  someone else would be confused by it as well.
+- After a round of line notes, it can be helpful to post a summary note such as
+  "LGTM :thumbsup:", or "Just a couple things to address."
+- Avoid accepting a merge request before the build succeeds ("Merge when build
+  succeeds" is fine).
+
+## Credits
+
+Largely based on the [thoughtbot code review guide].
+
+[thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review
+
+---
+
+[Return to Development documentation](README.md)
diff --git a/doc/development/instrumentation.md b/doc/development/instrumentation.md
index c0192bd67094b587234be81d2c80fb3a82242db4..c1cf2e77c2689ec05187499af6a2750d56eae848 100644
--- a/doc/development/instrumentation.md
+++ b/doc/development/instrumentation.md
@@ -2,36 +2,35 @@
 
 GitLab Performance Monitoring allows instrumenting of custom blocks of Ruby
 code. This can be used to measure the time spent in a specific part of a larger
-chunk of code. The resulting data is written to a separate series.
+chunk of code. The resulting data is stored as a field in the transaction that
+executed the block.
 
-To start measuring a block of Ruby code you should use
-`Gitlab::Metrics.measure` and give it a name for the series to store the data
-in:
+To start measuring a block of Ruby code you should use `Gitlab::Metrics.measure`
+and give it a name:
 
 ```ruby
-Gitlab::Metrics.measure(:user_logins) do
+Gitlab::Metrics.measure(:foo) do
   ...
 end
 ```
 
-The first argument of this method is the series name and should be plural. This
-name will be prefixed with `rails_` or `sidekiq_` depending on whether the code
-was run in the Rails application or one of the Sidekiq workers. In the
-above example the final series names would be as follows:
+3 values are measured for a block:
 
-- rails_user_logins
-- sidekiq_user_logins
+1. The real time elapsed, stored in NAME_real_time.
+2. The CPU time elapsed, stored in NAME_cpu_time.
+3. The call count, stored in NAME_call_count.
 
-Series names should be plural as this keeps the naming style in line with the
-other series names.
+Both the real and CPU timings are measured in milliseconds.
 
-By default metrics measured using a block contain a single value, "duration",
-which contains the number of milliseconds it took to execute the block. Custom
-values can be added by passing a Hash as the 2nd argument. Custom tags can be
-added by passing a Hash as the 3rd argument. A simple example is as follows:
+Multiple calls to the same block will result in the final values being the sum
+of all individual values. Take this code for example:
 
 ```ruby
-Gitlab::Metrics.measure(:example_series, { number: 10 }, { class: self.class.to_s }) do
-  ...
+3.times do
+  Gitlab::Metrics.measure(:sleep) do
+    sleep 1
+  end
 end
 ```
+
+Here the final value of `sleep_real_time` will be `3`, _not_ `1`.
diff --git a/doc/development/testing.md b/doc/development/testing.md
new file mode 100644
index 0000000000000000000000000000000000000000..672e3fb4649a4f4233bcd9183bbc075f8b1972c1
--- /dev/null
+++ b/doc/development/testing.md
@@ -0,0 +1,136 @@
+# Testing Standards and Style Guidelines
+
+This guide outlines standards and best practices for automated testing of GitLab
+CE and EE.
+
+It is meant to be an _extension_ of the [thoughtbot testing
+styleguide](https://github.com/thoughtbot/guides/tree/master/style/testing). If
+this guide defines a rule that contradicts the thoughtbot guide, this guide
+takes precedence. Some guidelines may be repeated verbatim to stress their
+importance.
+
+## Factories
+
+GitLab uses [factory_girl] as a test fixture replacement.
+
+- Factory definitions live in `spec/factories/`, named using the pluralization
+  of their corresponding model (`User` factories are defined in `users.rb`).
+- There should be only one top-level factory definition per file.
+- FactoryGirl methods are mixed in to all RSpec groups. This means you can (and
+  should) call `create(...)` instead of `FactoryGirl.create(...)`.
+- Make use of [traits] to clean up definitions and usages.
+- When defining a factory, don't define attributes that are not required for the
+  resulting record to pass validation.
+- When instantiating from a factory, don't supply attributes that aren't
+  required by the test.
+- Factories don't have to be limited to `ActiveRecord` objects.
+  [See example](https://gitlab.com/gitlab-org/gitlab-ce/commit/0b8cefd3b2385a21cfed779bd659978c0402766d).
+
+[factory_girl]: https://github.com/thoughtbot/factory_girl
+[traits]: http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md#Traits
+
+## JavaScript
+
+GitLab uses [Teaspoon] to run its [Jasmine] JavaScript specs. They can be run on
+the command line via `bundle exec teaspoon`, or via a web browser at
+`http://localhost:3000/teaspoon` when the Rails server is running.
+
+- JavaScript tests live in `spec/javascripts/`, matching the folder structure of
+  `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js.coffee` has a corresponding
+  `spec/javascripts/behaviors/autosize_spec.js.coffee` file.
+- Haml fixtures required for JavaScript tests live in
+  `spec/javascripts/fixtures`. They should contain the bare minimum amount of
+  markup necessary for the test.
+
+    > **Warning:** Keep in mind that a Rails view may change and
+    invalidate your test, but everything will still pass because your fixture
+    doesn't reflect the latest view.
+
+- Keep in mind that in a CI environment, these tests are run in a headless
+  browser and you will not have access to certain APIs, such as
+  [`Notification`](https://developer.mozilla.org/en-US/docs/Web/API/notification),
+  which will have to be stubbed.
+
+[Teaspoon]: https://github.com/modeset/teaspoon
+[Jasmine]: https://github.com/jasmine/jasmine
+
+## RSpec
+
+### General Guidelines
+
+- Use a single, top-level `describe ClassName` block.
+- Use `described_class` instead of repeating the class name being described.
+- Use `.method` to describe class methods and `#method` to describe instance
+  methods.
+- Use `context` to test branching logic.
+- Don't `describe` symbols (see [Gotchas](gotchas.md#dont-describe-symbols)).
+- Prefer `not_to` to `to_not`.
+- Try to match the ordering of tests to the ordering within the class.
+- Try to follow the [Four-Phase Test][four-phase-test] pattern, using newlines
+  to separate phases.
+
+[four-phase-test]: https://robots.thoughtbot.com/four-phase-test
+
+### `let` variables
+
+GitLab's RSpec suite has made extensive use of `let` variables to reduce
+duplication. However, this sometimes [comes at the cost of clarity][lets-not],
+so we need to set some guidelines for their use going forward:
+
+- `let` variables are preferable to instance variables. Local variables are
+  preferable to `let` variables.
+- Use `let` to reduce duplication throughout an entire spec file.
+- Don't use `let` to define variables used by a single test; define them as
+  local variables inside the test's `it` block.
+- Don't define a `let` variable inside the top-level `describe` block that's
+  only used in a more deeply-nested `context` or `describe` block. Keep the
+  definition as close as possible to where it's used.
+- Try to avoid overriding the definition of one `let` variable with another.
+- Don't define a `let` variable that's only used by the definition of another.
+  Use a helper method instead.
+
+[lets-not]: https://robots.thoughtbot.com/lets-not
+
+### Test speed
+
+GitLab has a massive test suite that, without parallelization, can take more
+than an hour to run. It's important that we make an effort to write tests that
+are accurate and effective _as well as_ fast.
+
+Here are some things to keep in mind regarding test performance:
+
+- `double` and `spy` are faster than `FactoryGirl.build(...)`
+- `FactoryGirl.build(...)` and `.build_stubbed` are faster than `.create`.
+- Don't `create` an object when `build`, `build_stubbed`, `attributes_for`,
+  `spy`, or `double` will do. Database persistence is slow!
+- Use `create(:empty_project)` instead of `create(:project)` when you don't need
+  the underlying Git repository. Filesystem operations are slow!
+- Don't mark a feature as requiring JavaScript (through `@javascript` in
+  Spinach or `js: true` in RSpec) unless it's _actually_ required for the test
+  to be valid. Headless browser testing is slow!
+
+### Features / Integration
+
+- Feature specs live in `spec/features/` and should be named
+  `ROLE_ACTION_spec.rb`, such as `user_changes_password_spec.rb`.
+- Use only one `feature` block per feature spec file.
+- Use scenario titles that describe the success and failure paths.
+- Avoid scenario titles that add no information, such as "successfully."
+- Avoid scenario titles that repeat the feature title.
+
+## Spinach (feature) tests
+
+GitLab [moved from Cucumber to Spinach](https://github.com/gitlabhq/gitlabhq/pull/1426)
+for its feature/integration tests in September 2012.
+
+As of March 2016, we are [trying to avoid adding new Spinach
+tests](https://gitlab.com/gitlab-org/gitlab-ce/issues/14121) going forward,
+opting for [RSpec feature](#features-integration) specs.
+
+Adding new Spinach scenarios is acceptable _only if_ the new scenario requires
+no more than one new `step` definition. If more than that is required, the
+test should be re-implemented using RSpec instead.
+
+---
+
+[Return to Development documentation](README.md)
diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md
index 2f01defc11d8a7b474c02f825314b6c0fc7c9d0a..a3e260a5f89d2f340995777877ce88c20e261f3b 100644
--- a/doc/development/ui_guide.md
+++ b/doc/development/ui_guide.md
@@ -1,9 +1,5 @@
 # UI Guide for building GitLab 
 
-## Best practices for creating new pages in GitLab
-
-TODO: write some best practices when develop GitLab features.
-
 ## GitLab UI development kit
 
 We created a page inside GitLab where you can check commonly used html and css elements.
diff --git a/doc/integration/README.md b/doc/integration/README.md
index 7c8f785a61f1c26f0f5ce11e56e613b8c341f513..6fe04aa2a0639f40cb95331016b8db47a0aa9bce 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -19,26 +19,15 @@ See the documentation below for details on how to configure these services.
 
 GitLab Enterprise Edition contains [advanced Jenkins support][jenkins].
 
+[jenkins]: http://doc.gitlab.com/ee/integration/jenkins.html
+
+
 ## Project services
 
 Integration with services such as Campfire, Flowdock, Gemnasium, HipChat,
 Pivotal Tracker, and Slack are available in the form of a [Project Service][].
-You can find these within GitLab in the Services page under Project Settings if
-you are at least a master on the project.
-Project Services are a bit like plugins in that they allow a lot of freedom in
-adding functionality to GitLab. For example there is also a service that can
-send an email every time someone pushes new commits.
 
-Because GitLab is open source we can ship with the code and tests for all
-plugins. This allows the community to keep the plugins up to date so that they
-always work in newer GitLab versions.
-
-For an overview of what projects services are available without logging in,
-please see the [project_services directory][projects-code].
-
-[jenkins]: http://doc.gitlab.com/ee/integration/jenkins.html
 [Project Service]: ../project_services/project_services.md
-[projects-code]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/models/project_services
 
 ## SSL certificate errors
 
diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md
index 3fea2cff0b9e302061db0e05bb97c372bcac7c3b..a5af620d9be6f16ea4eeec20ffbcd3340397e111 100644
--- a/doc/project_services/project_services.md
+++ b/doc/project_services/project_services.md
@@ -1,7 +1,24 @@
 # Project Services
 
 Project services allow you to integrate GitLab with other applications. Below
-is list of the currently supported ones. Click on the service links to see
+is list of the currently supported ones.
+
+You can find these within GitLab in the Services page under Project Settings if
+you are at least a master on the project.
+Project Services are a bit like plugins in that they allow a lot of freedom in
+adding functionality to GitLab. For example there is also a service that can
+send an email every time someone pushes new commits.
+
+Because GitLab is open source we can ship with the code and tests for all
+plugins. This allows the community to keep the plugins up to date so that they
+always work in newer GitLab versions.
+
+For an overview of what projects services are available without logging in,
+please see the [project_services directory][projects-code].
+
+[projects-code]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/models/project_services
+
+Click on the service links to see
 further configuration instructions and details. Contributions are welcome.
 
 ## Services
diff --git a/features/groups.feature b/features/groups.feature
index 49e939807b5258addbd7494e4ff843794516af25..419a5d3963d6314b0c969653d85d4f3b4d4382ba 100644
--- a/features/groups.feature
+++ b/features/groups.feature
@@ -7,6 +7,10 @@ Feature: Groups
     When I visit group "NonExistentGroup" page
     Then page status code should be 404
 
+  Scenario: I should have back to group button
+    When I visit group "Owned" page
+    Then I should see back to dashboard button
+
   @javascript
   Scenario: I should see group "Owned" dashboard list
     When I visit group "Owned" page
diff --git a/features/project/forked_merge_requests.feature b/features/project/forked_merge_requests.feature
index 10bd6fec80373d7682281760535ac5d66f8e8581..67f1e117f7fa9671dc71cd0e2588f958074d6c86 100644
--- a/features/project/forked_merge_requests.feature
+++ b/features/project/forked_merge_requests.feature
@@ -4,6 +4,7 @@ Feature: Project Forked Merge Requests
     And I am a member of project "Shop"
     And I have a project forked off of "Shop" called "Forked Shop"
 
+  @javascript
   Scenario: I submit new unassigned merge request to a forked project
     Given I visit project "Forked Shop" merge requests page
     And I click link "New Merge Request"
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index 823658b4f240657b768e5d8f59a48ffed4bba3bf..ecda4ea824035665c6ba4b8718dd2119eb8c3b40 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -70,6 +70,7 @@ Feature: Project Merge Requests
     When I click link "Reopen"
     Then I should see reopened merge request "Bug NS-04"
 
+  @javascript
   Scenario: I submit new unassigned merge request
     Given I click link "New Merge Request"
     And I submit new merge request "Wiki Feature"
diff --git a/features/project/project.feature b/features/project/project.feature
index aa22401c88ec346dcca6abe3b0faa428f07ab46d..f1f3ed26065eb9cee2088fb0daa190c85506c58e 100644
--- a/features/project/project.feature
+++ b/features/project/project.feature
@@ -18,6 +18,15 @@ Feature: Project
     Then I should see the default project avatar
     And I should not see the "Remove avatar" button
 
+  Scenario: I should have back to group button
+    And project "Shop" belongs to group
+    And I visit project "Shop" page
+    Then I should see back to group button
+
+  Scenario: I should have back to group button
+    And I visit project "Shop" page
+    Then I should see back to dashboard button
+
   Scenario: I should have readme on page
     And I visit project "Shop" page
     Then I should see project "Shop" README
diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb
index b6ce5bc9cec70b8e840b627f97b52e27fb462fe3..a167d25983777be8309af1c035edf1646d00d693 100644
--- a/features/steps/group/milestones.rb
+++ b/features/steps/group/milestones.rb
@@ -5,9 +5,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
   include SharedUser
 
   step 'I click on group milestones' do
-    page.within '.nav-secondary' do
-      click_link("Milestones")
-    end
+    click_link 'Milestones'
   end
 
   step 'I should see group milestones index page has no milestones' do
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 483370f41c6f775cc43f5081aacd392ff96bbd60..e5b7db4c5e39a5157f7751dd760c1f49fe0183d4 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -4,6 +4,10 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
   include SharedGroup
   include SharedUser
 
+  step 'I should see back to dashboard button' do
+    expect(page).to have_content 'Go to dashboard'
+  end
+
   step 'I should see group "Owned"' do
     expect(page).to have_content '@owned'
   end
diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb
index 4584fc4d75418f13b54f8c6740def94fab9cd30c..19d81453d8cd7f67cf32bf7847e969eec83f9287 100644
--- a/features/steps/project/active_tab.rb
+++ b/features/steps/project/active_tab.rb
@@ -82,9 +82,7 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
   # Sub Tabs: Issues
 
   step 'I click the "Milestones" tab' do
-    page.within '.nav-secondary' do
-      click_link('Milestones')
-    end
+    click_link('Milestones')
   end
 
   step 'I click the "Labels" tab' do
diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb
index d9b16afa9b8a8863428b3a2d8723911f33dc922b..527f7853da9f093a2ea9f1ddce752ec403f7a534 100644
--- a/features/steps/project/fork.rb
+++ b/features/steps/project/fork.rb
@@ -36,7 +36,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps
   end
 
   step 'I goto the Merge Requests page' do
-    page.within '.nav-secondary' do
+    page.within '.page-sidebar-expanded' do
       click_link "Merge Requests"
     end
   end
diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb
index 7e4425ff6625a988449464b49b1cba4f35dc0506..612bb8fd8b1e5d4f554cbaaedbda44505cb1c50e 100644
--- a/features/steps/project/forked_merge_requests.rb
+++ b/features/steps/project/forked_merge_requests.rb
@@ -34,10 +34,14 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
   end
 
   step 'I fill out a "Merge Request On Forked Project" merge request' do
-    select @forked_project.path_with_namespace, from: "merge_request_source_project_id"
-    select @project.path_with_namespace, from: "merge_request_target_project_id"
-    select "fix", from: "merge_request_source_branch"
-    select "master", from: "merge_request_target_branch"
+    first('.js-source-project').click
+    first('.dropdown-source-project a', text: @forked_project.path_with_namespace)
+
+    first('.js-target-project').click
+    first('.dropdown-target-project a', text: @project.path_with_namespace)
+
+    first('.js-source-branch').click
+    first('.dropdown-source-branch .dropdown-content a', text: 'fix').click
 
     click_button "Compare branches and continue"
 
@@ -115,10 +119,10 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
   end
 
   step 'I fill out an invalid "Merge Request On Forked Project" merge request' do
-    expect(find(:select, "merge_request_source_project_id", {}).value).to eq @forked_project.id.to_s
-    expect(find(:select, "merge_request_target_project_id", {}).value).to eq @project.id.to_s
-    expect(find(:select, "merge_request_source_branch", {}).value).to eq ""
-    expect(find(:select, "merge_request_target_branch", {}).value).to eq "master"
+    expect(find_by_id("merge_request_source_project_id", visible: false).value).to eq @forked_project.id.to_s
+    expect(find_by_id("merge_request_target_project_id", visible: false).value).to eq @project.id.to_s
+    expect(find_by_id("merge_request_source_branch", visible: false).value).to eq nil
+    expect(find_by_id("merge_request_target_branch", visible: false).value).to eq "master"
     click_button "Compare branches"
   end
 
@@ -127,7 +131,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
   end
 
   step 'the target repository should be the original repository' do
-    expect(page).to have_select("merge_request_target_project_id", selected: @project.path_with_namespace)
+    expect(find_by_id("merge_request_target_project_id").value).to eq "#{@project.id}"
   end
 
   step 'I click "Assign to" dropdown"' do
diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb
index 2ab8956867bd780ba1e83b323bc36baa3aa57bb8..0ca2d6257c3bc889c9b095f6437f070d0685b0a2 100644
--- a/features/steps/project/issues/labels.rb
+++ b/features/steps/project/issues/labels.rb
@@ -15,7 +15,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
 
   step 'I delete all labels' do
     page.within '.labels' do
-      page.all('.btn-remove').each do |remove|
+      page.all('.remove-row').each do |remove|
         remove.click
         sleep 0.05
       end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index a4f02b590ea87ad23fb3619a4e0b252140812a9c..f0af0d097fa4957ee7662ea7256403f784efbc34 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -93,8 +93,12 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   step 'I submit new merge request "Wiki Feature"' do
-    select "fix", from: "merge_request_source_branch"
-    select "feature", from: "merge_request_target_branch"
+    find('.js-source-branch').click
+    find('.dropdown-source-branch .dropdown-content a', text: 'fix').click
+
+    find('.js-target-branch').click
+    first('.dropdown-target-branch .dropdown-content a', text: 'feature').click
+
     click_button "Compare branches"
     fill_in "merge_request_title", with: "Wiki Feature"
     click_button "Submit merge request"
diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb
index 8f1d4a223a9742f21c8bcdaf89b9c4626385bd63..ef185861e00e854a6a2e7b16ce2230f555602df0 100644
--- a/features/steps/project/project.rb
+++ b/features/steps/project/project.rb
@@ -114,9 +114,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps
   end
 
   step 'I should not see "Snippets" button' do
-    page.within '.nav-secondary' do
-      expect(page).not_to have_link 'Snippets'
-    end
+    expect(page).not_to have_link 'Snippets'
   end
 
   step 'project "Shop" belongs to group' do
@@ -125,6 +123,14 @@ class Spinach::Features::Project < Spinach::FeatureSteps
     @project.save!
   end
 
+  step 'I should see back to dashboard button' do
+    expect(page).to have_content 'Go to dashboard'
+  end
+
+  step 'I should see back to group button' do
+    expect(page).to have_content 'Go to group'
+  end
+
   step 'I click notifications drop down button' do
     click_link 'notifications-button'
   end
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index 243469b8e7d55f0eb237a70e0581604ed85e65aa..e072505e5d7bec13e6807fa9921575e5b6741c8e 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -213,13 +213,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
   end
 
   step 'I see Browse file link' do
-    expect(page).to have_link 'Browse File »'
-    expect(page).not_to have_link 'Browse Files »'
+    expect(page).to have_link 'Browse File'
+    expect(page).not_to have_link 'Browse Files'
   end
 
   step 'I see Browse code link' do
-    expect(page).to have_link 'Browse Files »'
-    expect(page).not_to have_link 'Browse File »'
+    expect(page).to have_link 'Browse Files'
     expect(page).not_to have_link 'Browse Directory »'
   end
 
diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb
index 32c3e99f45033d809d55fa82bfc85985f952cdaf..1448c3f44cc1d191c221ea51263495eaff7716c4 100644
--- a/features/steps/shared/diff_note.rb
+++ b/features/steps/shared/diff_note.rb
@@ -155,7 +155,7 @@ module SharedDiffNote
 
   step 'I should see a discussion reply button' do
     page.within(diff_file_selector) do
-      expect(page).to have_button('Reply')
+      expect(page).to have_button('Reply...')
     end
   end
 
diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb
index fa7d24ce611a9dc6c5f5bec685d9b10c95ed187c..4fc2ece79ff927ba2e526490126c8b4e6d8f251f 100644
--- a/features/steps/shared/project_tab.rb
+++ b/features/steps/shared/project_tab.rb
@@ -41,7 +41,7 @@ module SharedProjectTab
   end
 
   step 'the active main tab should be Settings' do
-    page.within '.nav-secondary' do
+    page.within '.nav-sidebar' do
       expect(page).to have_content('Go to project')
     end
   end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 4c49442bf8bf04d349524a02b8bb736e2d089d42..d76b46b8836c42936a065f63909246f0d6220416 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -170,6 +170,10 @@ module API
       expose :label_names, as: :labels
       expose :milestone, using: Entities::Milestone
       expose :assignee, :author, using: Entities::UserBasic
+
+      expose :subscribed do |issue, options|
+        issue.subscribed?(options[:current_user])
+      end
     end
 
     class MergeRequest < ProjectEntity
@@ -183,6 +187,10 @@ module API
       expose :milestone, using: Entities::Milestone
       expose :merge_when_build_succeeds
       expose :merge_status
+
+      expose :subscribed do |merge_request, options|
+        merge_request.subscribed?(options[:current_user])
+      end
     end
 
     class MergeRequestChanges < MergeRequest
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 1fee1dee1a6780c419e376a2a4be417cacc409cc..c4ea05ee6cf3c0c31cba274f880213c0e7d3c502 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -55,7 +55,7 @@ module API
         issues = filter_issues_state(issues, params[:state]) unless params[:state].nil?
         issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil?
         issues.reorder(issuable_order_by => issuable_sort)
-        present paginate(issues), with: Entities::Issue
+        present paginate(issues), with: Entities::Issue, current_user: current_user
       end
     end
 
@@ -92,7 +92,7 @@ module API
         end
 
         issues.reorder(issuable_order_by => issuable_sort)
-        present paginate(issues), with: Entities::Issue
+        present paginate(issues), with: Entities::Issue, current_user: current_user
       end
 
       # Get a single project issue
@@ -105,7 +105,7 @@ module API
       get ":id/issues/:issue_id" do
         @issue = user_project.issues.find(params[:issue_id])
         not_found! unless can?(current_user, :read_issue, @issue)
-        present @issue, with: Entities::Issue
+        present @issue, with: Entities::Issue, current_user: current_user
       end
 
       # Create a new project issue
@@ -149,7 +149,7 @@ module API
             issue.add_labels_by_names(params[:labels].split(','))
           end
 
-          present issue, with: Entities::Issue
+          present issue, with: Entities::Issue, current_user: current_user
         else
           render_validation_error!(issue)
         end
@@ -189,7 +189,7 @@ module API
             issue.add_labels_by_names(params[:labels].split(','))
           end
 
-          present issue, with: Entities::Issue
+          present issue, with: Entities::Issue, current_user: current_user
         else
           render_validation_error!(issue)
         end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 93052fba06be2213fcaa61898ff354ada37b0c50..4e7de8867b43aeedd564d088876d1c9cbb037b55 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -56,7 +56,7 @@ module API
           end
 
         merge_requests = merge_requests.reorder(issuable_order_by => issuable_sort)
-        present paginate(merge_requests), with: Entities::MergeRequest
+        present paginate(merge_requests), with: Entities::MergeRequest, current_user: current_user
       end
 
       # Create MR
@@ -94,7 +94,7 @@ module API
             merge_request.add_labels_by_names(params[:labels].split(","))
           end
 
-          present merge_request, with: Entities::MergeRequest
+          present merge_request, with: Entities::MergeRequest, current_user: current_user
         else
           handle_merge_request_errors! merge_request.errors
         end
@@ -130,7 +130,7 @@ module API
 
           authorize! :read_merge_request, merge_request
 
-          present merge_request, with: Entities::MergeRequest
+          present merge_request, with: Entities::MergeRequest, current_user: current_user
         end
 
         # Show MR commits
@@ -162,7 +162,7 @@ module API
           merge_request = user_project.merge_requests.
             find(params[:merge_request_id])
           authorize! :read_merge_request, merge_request
-          present merge_request, with: Entities::MergeRequestChanges
+          present merge_request, with: Entities::MergeRequestChanges, current_user: current_user
         end
 
         # Update MR
@@ -204,7 +204,7 @@ module API
               merge_request.add_labels_by_names(params[:labels].split(","))
             end
 
-            present merge_request, with: Entities::MergeRequest
+            present merge_request, with: Entities::MergeRequest, current_user: current_user
           else
             handle_merge_request_errors! merge_request.errors
           end
@@ -246,7 +246,7 @@ module API
               execute(merge_request)
           end
 
-          present merge_request, with: Entities::MergeRequest
+          present merge_request, with: Entities::MergeRequest, current_user: current_user
         end
 
         # Cancel Merge if Merge When build succeeds is enabled
@@ -325,7 +325,7 @@ module API
         get "#{path}/closes_issues" do
           merge_request = user_project.merge_requests.find(params[:merge_request_id])
           issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
-          present paginate(issues), with: Entities::Issue
+          present paginate(issues), with: Entities::Issue, current_user: current_user
         end
       end
     end
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index afb6ffa36092315c0e11d262906d9a47289fcb2f..0f3f505fa05c8f9d03c7dfc1e400e1cc5bb2314f 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -103,7 +103,7 @@ module API
         authorize! :read_milestone, user_project
 
         @milestone = user_project.milestones.find(params[:milestone_id])
-        present paginate(@milestone.issues), with: Entities::Issue
+        present paginate(@milestone.issues), with: Entities::Issue, current_user: current_user
       end
 
     end
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index ae714c87dc54bef7bb9032af713e897b699f7081..c14a9c4c72218fea9baef0482a0dbc88f20c784c 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -19,8 +19,10 @@ module Banzai
       cache_key = full_cache_key(cache_key, context[:pipeline])
 
       if cache_key
-        Rails.cache.fetch(cache_key) do
-          cacheless_render(text, context)
+        Gitlab::Metrics.measure(:banzai_cached_render) do
+          Rails.cache.fetch(cache_key) do
+            cacheless_render(text, context)
+          end
         end
       else
         cacheless_render(text, context)
@@ -64,13 +66,15 @@ module Banzai
     private
 
     def self.cacheless_render(text, context = {})
-      result = render_result(text, context)
+      Gitlab::Metrics.measure(:banzai_cacheless_render) do
+        result = render_result(text, context)
 
-      output = result[:output]
-      if output.respond_to?(:to_html)
-        output.to_html
-      else
-        output.to_s
+        output = result[:output]
+        if output.respond_to?(:to_html)
+          output.to_html
+        else
+          output.to_s
+        end
       end
     end
 
diff --git a/lib/gitlab/exclusive_lease.rb b/lib/gitlab/exclusive_lease.rb
index c73eca832d75c119d69a97a2faee74d69f17de99..c2260a5f7acc806d29d9e6061237ad429fb5474a 100644
--- a/lib/gitlab/exclusive_lease.rb
+++ b/lib/gitlab/exclusive_lease.rb
@@ -43,7 +43,9 @@ module Gitlab
     # false if the lease is already taken.
     def try_obtain
       # Performing a single SET is atomic
-      !!redis.set(redis_key, '1', nx: true, ex: @timeout)
+      Gitlab::Redis.with do |redis|
+        !!redis.set(redis_key, '1', nx: true, ex: @timeout)
+      end
     end
 
     # No #cancel method. See comments above!
diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb
index 4a3f47b5a95632b481b1e3e0ce16b0770c23ef96..2a0a5629be55093c7ed13756e6115e7e072a18c7 100644
--- a/lib/gitlab/metrics.rb
+++ b/lib/gitlab/metrics.rb
@@ -74,24 +74,32 @@ module Gitlab
     #
     # Example:
     #
-    #     Gitlab::Metrics.measure(:find_by_username_timings) do
+    #     Gitlab::Metrics.measure(:find_by_username_duration) do
     #       User.find_by_username(some_username)
     #     end
     #
-    # series - The name of the series to store the data in.
-    # values - A Hash containing extra values to add to the metric.
-    # tags - A Hash containing extra tags to add to the metric.
+    # name - The name of the field to store the execution time in.
     #
     # Returns the value yielded by the supplied block.
-    def self.measure(series, values = {}, tags = {})
-      return yield unless Transaction.current
+    def self.measure(name)
+      trans = current_transaction
+
+      return yield unless trans
+
+      real_start = Time.now.to_f
+      cpu_start = System.cpu_time
 
-      start = Time.now.to_f
       retval = yield
-      duration = (Time.now.to_f - start) * 1000.0
-      values = values.merge(duration: duration)
 
-      Transaction.current.add_metric(series, values, tags)
+      cpu_stop = System.cpu_time
+      real_stop = Time.now.to_f
+
+      real_time = (real_stop - real_start) * 1000.0
+      cpu_time = cpu_stop - cpu_start
+
+      trans.increment("#{name}_real_time", real_time)
+      trans.increment("#{name}_cpu_time", cpu_time)
+      trans.increment("#{name}_call_count", 1)
 
       retval
     end
@@ -107,5 +115,11 @@ module Gitlab
           new(udp: { host: host, port: port })
       end
     end
+
+    private
+
+    def self.current_transaction
+      Transaction.current
+    end
   end
 end
diff --git a/lib/gitlab/metrics/metric.rb b/lib/gitlab/metrics/metric.rb
index 7ea9555cc8c62cda18a22f091c075cef0c8dffe1..1cd1ca30f701b28580bfa13b997eef4f2f2ccfda 100644
--- a/lib/gitlab/metrics/metric.rb
+++ b/lib/gitlab/metrics/metric.rb
@@ -2,6 +2,8 @@ module Gitlab
   module Metrics
     # Class for storing details of a single metric (label, value, etc).
     class Metric
+      JITTER_RANGE = 0.000001..0.001
+
       attr_reader :series, :values, :tags, :created_at
 
       # series - The name of the series (as a String) to store the metric in.
@@ -16,11 +18,29 @@ module Gitlab
 
       # Returns a Hash in a format that can be directly written to InfluxDB.
       def to_hash
+        # InfluxDB overwrites an existing point if a new point has the same
+        # series, tag set, and timestamp. In a highly concurrent environment
+        # this means that using the number of seconds since the Unix epoch is
+        # inevitably going to collide with another timestamp. For example, two
+        # Rails requests processed by different processes may end up generating
+        # metrics using the _exact_ same timestamp (in seconds).
+        #
+        # Due to the way InfluxDB is set up there's no solution to this problem,
+        # all we can do is lower the amount of collisions. We do this by using
+        # Time#to_f which returns the seconds as a Float providing greater
+        # accuracy. We then add a small random value that is large enough to
+        # distinguish most timestamps but small enough to not alter the amount
+        # of seconds.
+        #
+        # See https://gitlab.com/gitlab-com/operations/issues/175 for more
+        # information.
+        time = @created_at.to_f + rand(JITTER_RANGE)
+
         {
           series:    @series,
           tags:      @tags,
           values:    @values,
-          timestamp: @created_at.to_i * 1_000_000_000
+          timestamp: (time * 1_000_000_000).to_i
         }
       end
     end
diff --git a/lib/gitlab/metrics/subscribers/rails_cache.rb b/lib/gitlab/metrics/subscribers/rails_cache.rb
new file mode 100644
index 0000000000000000000000000000000000000000..49e5f86e6e6e8be719fc286992419c908fe7d279
--- /dev/null
+++ b/lib/gitlab/metrics/subscribers/rails_cache.rb
@@ -0,0 +1,39 @@
+module Gitlab
+  module Metrics
+    module Subscribers
+      # Class for tracking the total time spent in Rails cache calls
+      class RailsCache < ActiveSupport::Subscriber
+        attach_to :active_support
+
+        def cache_read(event)
+          increment(:cache_read_duration, event.duration)
+        end
+
+        def cache_write(event)
+          increment(:cache_write_duration, event.duration)
+        end
+
+        def cache_delete(event)
+          increment(:cache_delete_duration, event.duration)
+        end
+
+        def cache_exist?(event)
+          increment(:cache_exists_duration, event.duration)
+        end
+
+        def increment(key, duration)
+          return unless current_transaction
+
+          current_transaction.increment(:cache_duration, duration)
+          current_transaction.increment(key, duration)
+        end
+
+        private
+
+        def current_transaction
+          Transaction.current
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb
index 83371265278f6da3b93d1bb34bd0186771e6cc7f..a7d183b2f94a801f9c8f44d72034e35cf3edd312 100644
--- a/lib/gitlab/metrics/system.rb
+++ b/lib/gitlab/metrics/system.rb
@@ -30,6 +30,17 @@ module Gitlab
           0
         end
       end
+
+      # THREAD_CPUTIME is not supported on OS X
+      if Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID)
+        def self.cpu_time
+          Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond)
+        end
+      else
+        def self.cpu_time
+          Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond)
+        end
+      end
     end
   end
 end
diff --git a/lib/gitlab/redis.rb b/lib/gitlab/redis.rb
new file mode 100644
index 0000000000000000000000000000000000000000..319447669dc3e2244fab38e164be808ffab6cd70
--- /dev/null
+++ b/lib/gitlab/redis.rb
@@ -0,0 +1,48 @@
+module Gitlab
+  class Redis
+    CACHE_NAMESPACE = 'cache:gitlab'
+
+    attr_reader :url
+
+    # To be thread-safe we must be careful when writing the class instance
+    # variables @url and @pool. Because @pool depends on @url we need two
+    # mutexes to prevent deadlock.
+    URL_MUTEX = Mutex.new
+    POOL_MUTEX = Mutex.new
+    private_constant :URL_MUTEX, :POOL_MUTEX
+
+    def self.url
+      @url || URL_MUTEX.synchronize { @url = new.url }
+    end
+
+    def self.with
+      if @pool.nil?
+        POOL_MUTEX.synchronize do
+          @pool = ConnectionPool.new { ::Redis.new(url: url) }
+        end
+      end
+      @pool.with { |redis| yield redis }
+    end
+    
+    def self.redis_store_options
+      url = new.url
+      redis_config_hash = ::Redis::Store::Factory.extract_host_options_from_uri(url)
+      # Redis::Store does not handle Unix sockets well, so let's do it for them
+      redis_uri = URI.parse(url)
+      if redis_uri.scheme == 'unix'
+        redis_config_hash[:path] = redis_uri.path
+      end
+      redis_config_hash
+    end
+
+    def initialize(rails_env=nil)
+      rails_env ||= Rails.env
+      config_file = File.expand_path('../../../config/resque.yml', __FILE__)
+  
+      @url = "redis://localhost:6379"
+      if File.exists?(config_file)
+        @url =YAML.load_file(config_file)[rails_env]
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/redis_config.rb b/lib/gitlab/redis_config.rb
deleted file mode 100644
index 4949c6db5392b88b5fa1c5708957d7bf72251896..0000000000000000000000000000000000000000
--- a/lib/gitlab/redis_config.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-module Gitlab
-  class RedisConfig
-    attr_reader :url
-
-    def self.url
-      new.url
-    end
-    
-    def self.redis_store_options
-      url = new.url
-      redis_config_hash = Redis::Store::Factory.extract_host_options_from_uri(url)
-      # Redis::Store does not handle Unix sockets well, so let's do it for them
-      redis_uri = URI.parse(url)
-      if redis_uri.scheme == 'unix'
-        redis_config_hash[:path] = redis_uri.path
-      end
-      redis_config_hash
-    end
-
-    def initialize(rails_env=nil)
-      rails_env ||= Rails.env
-      config_file = File.expand_path('../../../config/resque.yml', __FILE__)
-  
-      @url = "redis://localhost:6379"
-      if File.exists?(config_file)
-        @url =YAML.load_file(config_file)[rails_env]
-      end
-    end
-  end
-end
diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake
index 51e746ef923637b9a11a8273c55fc465459eab05..2214f855200e4c8984ab3cb65b2cb549d9ad1833 100644
--- a/lib/tasks/cache.rake
+++ b/lib/tasks/cache.rake
@@ -4,18 +4,19 @@ namespace :cache do
 
   desc "GitLab | Clear redis cache"
   task :clear => :environment do
-    redis = Redis.new(url: Gitlab::RedisConfig.url)
-    cursor = REDIS_SCAN_START_STOP
-    loop do
-      cursor, keys = redis.scan(
-        cursor,
-        match: "#{Gitlab::REDIS_CACHE_NAMESPACE}*", 
-        count: CLEAR_BATCH_SIZE
-      )
-
-      redis.del(*keys) if keys.any?
-
-      break if cursor == REDIS_SCAN_START_STOP
+    Gitlab::Redis.with do |redis|
+      cursor = REDIS_SCAN_START_STOP
+      loop do
+        cursor, keys = redis.scan(
+          cursor,
+          match: "#{Gitlab::Redis::CACHE_NAMESPACE}*", 
+          count: CLEAR_BATCH_SIZE
+        )
+  
+        redis.del(*keys) if keys.any?
+  
+        break if cursor == REDIS_SCAN_START_STOP
+      end
     end
   end
 end
diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard_issues_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..39805da9d0bb3f8a35a6584d50cc34be7a4c0729
--- /dev/null
+++ b/spec/features/dashboard_issues_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+describe "Dashboard Issues filtering", feature: true, js: true do
+  let(:user)      { create(:user) }
+  let(:project)   { create(:project) }
+  let(:milestone) { create(:milestone, project: project) }
+
+  context 'filtering by milestone' do
+    before do
+      project.team << [user, :master]
+      login_as(user)
+
+      create(:issue, project: project, author: user, assignee: user)
+      create(:issue, project: project, author: user, assignee: user, milestone: milestone)
+
+      visit_issues
+    end
+
+    it 'should show all issues with no milestone' do
+      show_milestone_dropdown
+
+      click_link 'No Milestone'
+
+      expect(page).to have_selector('.issue', count: 1)
+    end
+
+    it 'should show all issues with any milestone' do
+      show_milestone_dropdown
+
+      click_link 'Any Milestone'
+
+      expect(page).to have_selector('.issue', count: 2)
+    end
+
+    it 'should show all issues with the selected milestone' do
+      show_milestone_dropdown
+
+      page.within '.dropdown-content' do
+        click_link milestone.title
+      end
+
+      expect(page).to have_selector('.issue', count: 1)
+    end
+  end
+
+  def show_milestone_dropdown
+    click_button 'Milestone'
+    expect(page).to have_selector('.dropdown-content', visible: true)
+  end
+
+  def visit_issues
+    visit issues_dashboard_path
+  end
+end
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..41af789aae273137dd6c4bdb2ca8377e5393d6e3
--- /dev/null
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -0,0 +1,64 @@
+require 'rails_helper'
+
+describe 'Awards Emoji', feature: true do
+  let!(:project)   { create(:project) }
+  let!(:user)      { create(:user) }
+
+  before do
+    project.team << [user, :master]
+    login_as(user)
+  end
+
+  describe 'Click award emoji from issue#show' do
+    let!(:issue) do
+      create(:issue,
+             author: @user,
+             assignee: @user,
+             project: project)
+    end
+
+    before do
+      visit namespace_project_issue_path(project.namespace, project, issue)
+    end
+
+    it 'should increment the thumbsdown emoji', js: true do
+      find('[data-emoji="thumbsdown"]').click
+      sleep 2
+      expect(thumbsdown_emoji).to have_text("1")
+    end
+
+    context 'click the thumbsup emoji' do
+
+      it 'should increment the thumbsup emoji', js: true do
+        find('[data-emoji="thumbsup"]').click
+        sleep 2
+        expect(thumbsup_emoji).to have_text("1")
+      end
+
+      it 'should decrement the thumbsdown emoji', js: true do
+        expect(thumbsdown_emoji).to have_text("0")
+      end
+    end
+
+    context 'click the thumbsdown emoji' do
+
+      it 'should increment the thumbsdown emoji', js: true do
+        find('[data-emoji="thumbsdown"]').click
+        sleep 2
+        expect(thumbsdown_emoji).to have_text("1")
+      end
+
+      it 'should decrement the thumbsup emoji', js: true do
+        expect(thumbsup_emoji).to have_text("0")
+      end
+    end
+  end
+
+  def thumbsup_emoji
+    page.all('span.js-counter').first
+  end
+
+  def thumbsdown_emoji
+    page.all('span.js-counter').last
+  end
+end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index fd02d584848ad96e94ee1fa525bcd1c13defa13b..00b60bd0e7564551a175c7bc64f908de34ecef67 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-feature 'Create New Merge Request', feature: true, js: false do
+feature 'Create New Merge Request', feature: true, js: true do
   let(:user) { create(:user) }
   let(:project) { create(:project, :public) }
 
@@ -13,9 +13,12 @@ feature 'Create New Merge Request', feature: true, js: false do
 
   it 'generates a diff for an orphaned branch' do
     click_link 'New Merge Request'
-    select "orphaned-branch", from: "merge_request_source_branch"
-    select "master", from: "merge_request_target_branch"
+
+    first('.js-source-branch').click
+    first('.dropdown-source-branch .dropdown-content a', text: 'orphaned-branch').click
+
     click_button "Compare branches"
+    click_link "Changes"
 
     expect(page).to have_content "README.md"
     expect(page).to have_content "wm.png"
@@ -23,6 +26,8 @@ feature 'Create New Merge Request', feature: true, js: false do
     fill_in "merge_request_title", with: "Orphaned MR test"
     click_button "Submit merge request"
 
+    click_link "Check out branch"
+
     expect(page).to have_content 'git checkout -b orphaned-branch origin/orphaned-branch'
   end
 end
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index 70d0864783de0e4bca99f039e422154ab227e847..5f855ccc701b532c29f4e25b7fa9da2b4a3f640e 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -210,7 +210,7 @@ describe 'Comments', feature: true do
           is_expected.to have_content('Another comment on line 10')
           is_expected.to have_css('.notes_holder')
           is_expected.to have_css('.notes_holder .note', count: 1)
-          is_expected.to have_button('Reply')
+          is_expected.to have_button('Reply...')
         end
       end
     end
diff --git a/spec/helpers/form_helper_spec.rb b/spec/helpers/form_helper_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b20373a96fb4bef8a218882d4b84ab0d6916a2e3
--- /dev/null
+++ b/spec/helpers/form_helper_spec.rb
@@ -0,0 +1,46 @@
+require 'rails_helper'
+
+describe FormHelper do
+  describe 'form_errors' do
+    it 'returns nil when model has no errors' do
+      model = double(errors: [])
+
+      expect(helper.form_errors(model)).to be_nil
+    end
+
+    it 'renders an alert div' do
+      model = double(errors: errors_stub('Error 1'))
+
+      expect(helper.form_errors(model)).
+        to include('<div class="alert alert-danger" id="error_explanation">')
+    end
+
+    it 'contains a summary message' do
+      single_error = double(errors: errors_stub('A'))
+      multi_errors = double(errors: errors_stub('A', 'B', 'C'))
+
+      expect(helper.form_errors(single_error)).
+        to include('<h4>The form contains the following error:')
+      expect(helper.form_errors(multi_errors)).
+        to include('<h4>The form contains the following errors:')
+    end
+
+    it 'renders each message' do
+      model = double(errors: errors_stub('Error 1', 'Error 2', 'Error 3'))
+
+      errors = helper.form_errors(model)
+
+      aggregate_failures do
+        expect(errors).to include('<li>Error 1</li>')
+        expect(errors).to include('<li>Error 2</li>')
+        expect(errors).to include('<li>Error 3</li>')
+      end
+    end
+
+    def errors_stub(*messages)
+      ActiveModel::Errors.new(double).tap do |errors|
+        messages.each { |msg| errors.add(:base, msg) }
+      end
+    end
+  end
+end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 9adcd916ced3e81ca2504c5fa1b8e108d2df6cab..13de88e2f21e475ab577fd6e5036e945a38500af 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -150,13 +150,6 @@ describe GitlabMarkdownHelper do
     end
   end
 
-  describe 'random_markdown_tip' do
-    it 'returns a random Markdown tip' do
-      stub_const("#{described_class}::MARKDOWN_TIPS", ['Random tip'])
-      expect(random_markdown_tip).to eq 'Random tip'
-    end
-  end
-
   describe '#first_line_in_markdown' do
     let(:text) { "@#{user.username}, can you look at this?\nHello world\n"}
 
diff --git a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e01b0b4bd21b1e89f6f47761e53afded2ae43caf
--- /dev/null
+++ b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+
+describe Gitlab::Metrics::Subscribers::RailsCache do
+  let(:transaction) { Gitlab::Metrics::Transaction.new }
+  let(:subscriber) { described_class.new }
+
+  let(:event) { double(:event, duration: 15.2) }
+
+  describe '#cache_read' do
+    it 'increments the cache_read duration' do
+      expect(subscriber).to receive(:increment).
+        with(:cache_read_duration, event.duration)
+
+      subscriber.cache_read(event)
+    end
+  end
+
+  describe '#cache_write' do
+    it 'increments the cache_write duration' do
+      expect(subscriber).to receive(:increment).
+        with(:cache_write_duration, event.duration)
+
+      subscriber.cache_write(event)
+    end
+  end
+
+  describe '#cache_delete' do
+    it 'increments the cache_delete duration' do
+      expect(subscriber).to receive(:increment).
+        with(:cache_delete_duration, event.duration)
+
+      subscriber.cache_delete(event)
+    end
+  end
+
+  describe '#cache_exist?' do
+    it 'increments the cache_exists duration' do
+      expect(subscriber).to receive(:increment).
+        with(:cache_exists_duration, event.duration)
+
+      subscriber.cache_exist?(event)
+    end
+  end
+
+  describe '#increment' do
+    context 'without a transaction' do
+      it 'returns' do
+        expect(transaction).not_to receive(:increment)
+
+        subscriber.increment(:foo, 15.2)
+      end
+    end
+
+    context 'with a transaction' do
+      before do
+        allow(subscriber).to receive(:current_transaction).
+          and_return(transaction)
+      end
+
+      it 'increments the total and specific cache duration' do
+        expect(transaction).to receive(:increment).
+          with(:cache_duration, event.duration)
+
+        expect(transaction).to receive(:increment).
+          with(:cache_delete_duration, event.duration)
+
+        subscriber.increment(:cache_delete_duration, event.duration)
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb
index f8c1d956ca1fb9e0c5cb41d2e7e2b6b7e217dfbd..d6ae54e25e859830a534a11ed47eea2dc8f23f32 100644
--- a/spec/lib/gitlab/metrics/system_spec.rb
+++ b/spec/lib/gitlab/metrics/system_spec.rb
@@ -26,4 +26,10 @@ describe Gitlab::Metrics::System do
       end
     end
   end
+
+  describe '.cpu_time' do
+    it 'returns a Fixnum' do
+      expect(described_class.cpu_time).to be_an_instance_of(Fixnum)
+    end
+  end
 end
diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb
index 8f63a5f2043d6da59c675b1d43b12daf434b90a7..3dee13e27f442d2b7c5cba896f175c0f36b3062a 100644
--- a/spec/lib/gitlab/metrics_spec.rb
+++ b/spec/lib/gitlab/metrics_spec.rb
@@ -74,24 +74,21 @@ describe Gitlab::Metrics do
       let(:transaction) { Gitlab::Metrics::Transaction.new }
 
       before do
-        allow(Gitlab::Metrics::Transaction).to receive(:current).
+        allow(Gitlab::Metrics).to receive(:current_transaction).
           and_return(transaction)
       end
 
       it 'adds a metric to the current transaction' do
-        expect(transaction).to receive(:add_metric).
-          with(:foo, { duration: a_kind_of(Numeric) }, { tag: 'value' })
+        expect(transaction).to receive(:increment).
+          with('foo_real_time', a_kind_of(Numeric))
 
-        Gitlab::Metrics.measure(:foo, {}, tag: 'value') { 10 }
-      end
-
-      it 'supports adding of custom values' do
-        values = { duration: a_kind_of(Numeric), number: 10 }
+        expect(transaction).to receive(:increment).
+          with('foo_cpu_time', a_kind_of(Numeric))
 
-        expect(transaction).to receive(:add_metric).
-          with(:foo, values, { tag: 'value' })
+        expect(transaction).to receive(:increment).
+          with('foo_call_count', 1)
 
-        Gitlab::Metrics.measure(:foo, { number: 10 }, tag: 'value') { 10 }
+        Gitlab::Metrics.measure(:foo) { 10 }
       end
 
       it 'returns the return value of the block' do