diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c3b864f16cfc46a30c4997ddac6dd38d100f709e..ace0de5cad3db69220c873bb934fdb9ef06febe5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -99,7 +99,7 @@ update-knapsack:
     - export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
     - export KNAPSACK_GENERATE_REPORT=true
     - cp knapsack/spinach_report.json ${KNAPSACK_REPORT_PATH}
-    - knapsack spinach "-r rerun" || retry '[ ! -e tmp/spinach-rerun.txt ] || bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
+    - knapsack spinach "-r rerun" || retry '[[ -e tmp/spinach-rerun.txt ]] && bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
   artifacts:
     expire_in: 31d
     paths:
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index d2a1eb564237e00473a2e5ec7d1a38d346f5d7a8..9b541aadad1e28d4b30d8eb18b218e257d99a459 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -1,4 +1,4 @@
-See the general Documentation guidelines http://docs.gitlab.com/ce/development/doc_styleguide.html.
+See the general Documentation guidelines http://docs.gitlab.com/ce/development/doc_styleguide.html
 
 ## What does this MR do?
 
diff --git a/.scss-lint.yml b/.scss-lint.yml
index 71df6be6a155236f85703ecb213395dccd59c515..5093702519bd0d51379d6e7f9c02e48b4f769dc6 100644
--- a/.scss-lint.yml
+++ b/.scss-lint.yml
@@ -61,7 +61,7 @@ linters:
   
   # Separate rule, function, and mixin declarations with empty lines.
   EmptyLineBetweenBlocks:
-    enabled: false
+    enabled: true
   
   # Reports when you have an empty rule set.
   EmptyRule:
@@ -219,7 +219,7 @@ linters:
   # Property values, @extend, @include, and @import directives, and variable
   # declarations should always end with a semicolon.
   TrailingSemicolon:
-    enabled: false
+    enabled: true
 
   # Reports lines containing trailing whitespace.
   TrailingWhitespace:
diff --git a/CHANGELOG b/CHANGELOG
index 99bbd99726d5c736fcc5901548941c7149e620b1..e3201cd22504fcf2b6c303bf5478da4f5b4392e3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,8 @@ v 8.13.0 (unreleased)
   - Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675)
   - Respond with 404 Not Found for non-existent tags (Linus Thiel)
   - Truncate long labels with ellipsis in labels page
+  - Improve tabbing usability for sign in page (ClemMakesApps)
+  - Enforce TrailingSemicolon and EmptyLineBetweenBlocks in scss-lint
   - Adding members no longer silently fails when there is extra whitespace
   - Update runner version only when updating contacted_at
   - Add link from system note to compare with previous version
@@ -14,6 +16,7 @@ v 8.13.0 (unreleased)
   - Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun)
   - Updating verbiage on git basics to be more intuitive
   - Clarify documentation for Runners API (Gennady Trafimenkov)
+  - The instrumentation for Banzai::Renderer has been restored
   - Change user & group landing page routing from /u/:username to /:username
   - Prevent running GfmAutocomplete setup for each diff note !6569
   - Added documentation for .gitattributes files
@@ -24,6 +27,7 @@ v 8.13.0 (unreleased)
   - Create a new /templates namespace for the /licenses, /gitignores and /gitlab_ci_ymls API endpoints. !5717 (tbalthazar)
   - Speed-up group milestones show page
   - Fix inconsistent options dropdown caret on mobile viewports (ClemMakesApps)
+  - Extract project#update_merge_requests and SystemHooks to its own worker from GitPushService
   - Don't include archived projects when creating group milestones. !4940 (Jeroen Jacobs)
   - Add tag shortcut from the Commit page. !6543
   - Keep refs for each deployment
@@ -50,6 +54,7 @@ v 8.13.0 (unreleased)
   - Add new issue button to each list on Issues Board
   - Added soft wrap button to repository file/blob editor
   - Update namespace validation to forbid reserved names (.git and .atom) (Will Starms)
+  - Show the time ago a merge request was deployed to an environment
   - Add word-wrap to issue title on issue and milestone boards (ClemMakesApps)
   - Fix todos page mobile viewport layout (ClemMakesApps)
   - Fix inconsistent highlighting of already selected activity nav-links (ClemMakesApps)
@@ -112,10 +117,12 @@ v 8.13.0 (unreleased)
   - Fixes padding in all clipboard icons that have .btn class
   - Fix a typo in doc/api/labels.md
   - API: all unknown routing will be handled with 404 Not Found
+  - Add docs for request profiling
   - Make guests unable to view MRs on private projects
 
 v 8.12.7
   - Use gitlab-markup gem instead of github-markup to fix `.rst` file rendering. !6659
+  - Fix GFM autocomplete setup being called several times
 
 v 8.12.6
   - Update mailroom to 0.8.1 in Gemfile.lock  !6814
@@ -263,6 +270,7 @@ v 8.12.0
   - Remove prefixes from transition CSS property (ClemMakesApps)
   - Add Sentry logging to API calls
   - Add BroadcastMessage API
+  - Merge request tabs are fixed when scrolling page
   - Use 'git update-ref' for safer web commits !6130
   - Sort pipelines requested through the API
   - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
@@ -369,6 +377,7 @@ v 8.11.7
   - Avoid conflict with admin labels when importing GitHub labels. !6158
   - Restores `fieldName` to allow only string values in `gl_dropdown.js`. !6234
   - Allow the Rails cookie to be used for API authentication.
+  - Login/Register UX upgrade !6328
 
 v 8.11.6
   - Fix unnecessary horizontal scroll area in pipeline visualizations. !6005
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index b60d71966ae916fb42407607e33427d305ad69b3..7ada0d303f3e7e49c3f18bfa9dcfa73a1895b28b 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.8.4
+0.8.5
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index f336bfc36d619e01470277deb6b11ccbe6fb01dd..97462a5959c0d8262cddfb7c1b9d255b50764058 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -15,18 +15,17 @@
       this.hideSidebar = bind(this.hideSidebar, this);
       this.toggleSidebar = bind(this.toggleSidebar, this);
       this.updateDropdown = bind(this.updateDropdown, this);
+      this.$document = $(document);
       clearInterval(Build.interval);
       // Init breakpoint checker
       this.bp = Breakpoints.get();
-      $('.js-build-sidebar').niceScroll();
+      this.initSidebar();
 
       this.populateJobs(this.build_stage);
       this.updateStageDropdownText(this.build_stage);
-      this.hideSidebar();
 
-      $(document).off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
       $(window).off('resize.build').on('resize.build', this.hideSidebar);
-      $(document).off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
+      this.$document.off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
       $('#js-build-scroll > a').off('click').on('click', this.stepTrace);
       this.updateArtifactRemoveDate();
       if ($('#build-trace').length) {
@@ -62,6 +61,21 @@
       }
     }
 
+    Build.prototype.initSidebar = function() {
+      this.$sidebar = $('.js-build-sidebar');
+      this.sidebarTranslationLimits = {
+        min: $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()
+      }
+      this.sidebarTranslationLimits.max = this.sidebarTranslationLimits.min + $('.scrolling-tabs-container').outerHeight();
+      this.$sidebar.css({
+        top: this.sidebarTranslationLimits.max
+      });
+      this.$sidebar.niceScroll();
+      this.hideSidebar();
+      this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
+      this.$document.off('scroll.translateSidebar').on('scroll.translateSidebar', this.translateSidebar.bind(this));
+    };
+
     Build.prototype.getInitialBuildTrace = function() {
       var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped']
 
@@ -129,15 +143,23 @@
 
     Build.prototype.toggleSidebar = function() {
       if (this.shouldHideSidebar()) {
-        return $('.js-build-sidebar').toggleClass('right-sidebar-expanded right-sidebar-collapsed');
+        return this.$sidebar.toggleClass('right-sidebar-expanded right-sidebar-collapsed');
       }
     };
 
+    Build.prototype.translateSidebar = function(e) {
+      var newPosition = this.sidebarTranslationLimits.max - document.body.scrollTop;
+      if (newPosition < this.sidebarTranslationLimits.min) newPosition = this.sidebarTranslationLimits.min;
+      this.$sidebar.css({
+        top: newPosition
+      });
+    };
+
     Build.prototype.hideSidebar = function() {
       if (this.shouldHideSidebar()) {
-        return $('.js-build-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
+        return this.$sidebar.removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
       } else {
-        return $('.js-build-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
+        return this.$sidebar.removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
       }
     };
 
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js.es6
similarity index 96%
rename from app/assets/javascripts/dispatcher.js
rename to app/assets/javascripts/dispatcher.js.es6
index f3ef13ce20e9fe9c7a5a6e98db8dd3c12566cee5..f3957ed374b54b020802180a55a558a9b0674898 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -8,6 +8,7 @@
   Dispatcher = (function() {
     function Dispatcher() {
       this.initSearch();
+      this.initFieldErrors();
       this.initPageScripts();
     }
 
@@ -20,6 +21,9 @@
       path = page.split(':');
       shortcut_handler = null;
       switch (page) {
+        case 'sessions:new':
+          new UsernameValidator();
+          break;
         case 'projects:boards:show':
         case 'projects:boards:index':
           shortcut_handler = new ShortcutsNavigation();
@@ -140,12 +144,12 @@
           break;
         case 'groups:group_members:index':
           new gl.MemberExpirationDate();
-          new GroupMembers();
+          new gl.Members();
           new UsersSelect();
           break;
         case 'projects:project_members:index':
           new gl.MemberExpirationDate();
-          new ProjectMembers();
+          new gl.Members();
           new UsersSelect();
           break;
         case 'groups:new':
@@ -291,6 +295,12 @@
       }
     };
 
+    Dispatcher.prototype.initFieldErrors = function() {
+      $('.show-gl-field-errors').each((i, form) => {
+        new gl.GlFieldErrors(form);
+      });
+    };
+
     return Dispatcher;
 
   })();
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index e034ca6864561626da835ec9d17a79d176e395bb..53762f2965cd33d108158149483f248379f4f2da 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -25,7 +25,7 @@
         return function(e) {
           e.preventDefault();
           e.stopPropagation();
-          return _this.input.val('').trigger('keyup').focus();
+          return _this.input.val('').trigger('input').focus();
         };
       })(this));
       // Key events
@@ -37,28 +37,16 @@
             e.preventDefault()
           }
         })
-        .on('keyup', function(e) {
-          var keyCode;
-          keyCode = e.which;
-          if (ARROW_KEY_CODES.indexOf(keyCode) >= 0) {
-            return;
-          }
+        .on('input', function() {
           if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
             $inputContainer.addClass(HAS_VALUE_CLASS);
           } else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
             $inputContainer.removeClass(HAS_VALUE_CLASS);
           }
-          if (keyCode === 13 && !options.elIsInput) {
-            return false;
-          }
           // Only filter asynchronously only if option remote is set
           if (this.options.remote) {
             clearTimeout(timeout);
             return timeout = setTimeout(function() {
-              var blurField = this.shouldBlur(keyCode);
-              if (blurField && this.filterInputBlur) {
-                this.input.blur();
-              }
               return this.options.query(this.input.val(), function(data) {
                 return this.options.callback(data);
               }.bind(this));
@@ -255,7 +243,7 @@
                 _this.fullData = data;
                 _this.parseData(_this.fullData);
                 if (_this.options.filterable && _this.filter && _this.filter.input) {
-                  return _this.filter.input.trigger('keyup');
+                  return _this.filter.input.trigger('input');
                 }
               };
             // Remote data
@@ -487,7 +475,7 @@
       // Triggering 'keyup' will re-render the dropdown which is not always required
       // specially if we want to keep the state of the dropdown needed for bulk-assignment
       if (!this.options.persistWhenHide) {
-        $input.trigger("keyup");
+        $input.trigger("input");
       }
       if (this.dropdown.find(".dropdown-toggle-page").length) {
         $('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS);
@@ -500,14 +488,27 @@
 
     // Render the full menu
     GitLabDropdown.prototype.renderMenu = function(html) {
-      var menu_html;
-      menu_html = "";
       if (this.options.renderMenu) {
-        menu_html = this.options.renderMenu(html);
+        return this.options.renderMenu(html);
       } else {
-        menu_html = $('<ul />').append(html);
+        var ul = document.createElement('ul');
+
+        for (var i = 0; i < html.length; i++) {
+          var el = html[i];
+
+          if (el instanceof jQuery) {
+            el = el.get(0);
+          }
+
+          if (typeof el === 'string') {
+            ul.innerHTML += el;
+          } else {
+            ul.appendChild(el);
+          }
+        }
+
+        return ul;
       }
-      return menu_html;
     };
 
     // Append the menu into the dropdown
@@ -521,7 +522,7 @@
     };
 
     GitLabDropdown.prototype.renderItem = function(data, group, index) {
-      var cssClass, field, fieldName, groupAttrs, html, selected, text, url, value;
+      var field, fieldName, html, selected, text, url, value;
       if (group == null) {
         group = false;
       }
@@ -529,18 +530,16 @@
         // Render the row
         index = false;
       }
-      html = "";
-      // Divider
-      if (data === "divider") {
-        return "<li class='divider'></li>";
-      }
-      // Separator is a full-width divider
-      if (data === "separator") {
-        return "<li class='separator'></li>";
+      html = document.createElement('li');
+      if (data === 'divider' || data === 'separator') {
+        html.className = data;
+        return html;
       }
       // Header
       if (data.header != null) {
-        return _.template('<li class="dropdown-header"><%- header %></li>')({ header: data.header });
+        html.className = 'dropdown-header';
+        html.innerHTML = data.header;
+        return html;
       }
       if (this.options.renderRow) {
         // Call the render function
@@ -567,24 +566,25 @@
         } else {
           text = data.text != null ? data.text : '';
         }
-        cssClass = "";
-        if (selected) {
-          cssClass = "is-active";
-        }
         if (this.highlight) {
           text = this.highlightTextMatches(text, this.filterInput.val());
         }
+        // Create the list item & the link
+        var link = document.createElement('a');
+
+        link.href = url;
+        link.innerHTML = text;
+
+        if (selected) {
+          link.className = 'is-active';
+        }
+
         if (group) {
-          groupAttrs = 'data-group=' + group + ' data-index=' + index;
-        } else {
-          groupAttrs = '';
+          link.dataset.group = group;
+          link.dataset.index = index;
         }
-        html = _.template('<li><a href="<%- url %>" <%- groupAttrs %> class="<%- cssClass %>"><%= text %></a></li>')({
-          url: url,
-          groupAttrs: groupAttrs,
-          cssClass: cssClass,
-          text: text
-        });
+
+        html.appendChild(link);
       }
       return html;
     };
diff --git a/app/assets/javascripts/gl_field_errors.js.es6 b/app/assets/javascripts/gl_field_errors.js.es6
new file mode 100644
index 0000000000000000000000000000000000000000..8657e7b4abfa0da407249ae97efc553408f01b00
--- /dev/null
+++ b/app/assets/javascripts/gl_field_errors.js.es6
@@ -0,0 +1,167 @@
+((global) => {
+  /*
+   * This class overrides the browser's validation error bubbles, displaying custom
+   * error messages for invalid fields instead. To begin validating any form, add the
+   * class `show-gl-field-errors` to the form element, and ensure error messages are
+   * declared in each inputs' title attribute.
+   *
+   * Example:
+   *
+   * <form class='show-gl-field-errors'>
+   *  <input type='text' name='username' title='Username is required.'/>
+   *</form>
+   *
+    * */
+
+  const errorMessageClass = 'gl-field-error';
+  const inputErrorClass = 'gl-field-error-outline';
+
+  class GlFieldError {
+    constructor({ input, formErrors }) {
+      this.inputElement = $(input);
+      this.inputDomElement = this.inputElement.get(0);
+      this.form = formErrors;
+      this.errorMessage = this.inputElement.attr('title') || 'This field is required.';
+      this.fieldErrorElement = $(`<p class='${errorMessageClass} hide'>${ this.errorMessage }</p>`);
+
+      this.state = {
+        valid: false,
+        empty: true
+      };
+
+      this.initFieldValidation();
+    }
+
+    initFieldValidation() {
+      // hidden when injected into DOM
+      this.inputElement.after(this.fieldErrorElement);
+      this.inputElement.off('invalid').on('invalid', this.handleInvalidSubmit.bind(this));
+      this.scopedSiblings = this.safelySelectSiblings();
+    }
+
+    safelySelectSiblings() {
+      // Apply `ignoreSelector` in markup to siblings whose visibility should not be toggled with input validity
+      const ignoreSelector = '.validation-ignore';
+      const unignoredSiblings = this.inputElement.siblings(`p:not(${ignoreSelector})`);
+      const parentContainer = this.inputElement.parent('.form-group');
+
+      // Only select siblings when they're scoped within a form-group with one input
+      const safelyScoped = parentContainer.length && parentContainer.find('input').length === 1;
+
+      return safelyScoped ? unignoredSiblings : this.fieldErrorElement;
+    }
+
+    renderValidity() {
+      this.renderClear();
+
+      if (this.state.valid) {
+        return this.renderValid();
+      }
+
+      if (this.state.empty) {
+        return this.renderEmpty();
+      }
+
+      if (!this.state.valid) {
+        return this.renderInvalid();
+      }
+
+    }
+
+    handleInvalidSubmit(event) {
+      event.preventDefault();
+      const currentValue = this.accessCurrentValue();
+      this.state.valid = false;
+      this.state.empty = currentValue === '';
+
+      this.renderValidity();
+      this.form.focusOnFirstInvalid.apply(this.form);
+      // For UX, wait til after first invalid submission to check each keyup
+      this.inputElement.off('keyup.field_validator')
+        .on('keyup.field_validator', this.updateValidity.bind(this));
+
+    }
+
+    /* Get or set current input value */
+    accessCurrentValue(newVal) {
+      return newVal ? this.inputElement.val(newVal) : this.inputElement.val();
+    }
+
+    getInputValidity() {
+      return this.inputDomElement.validity.valid;
+    }
+
+    updateValidity() {
+      const inputVal = this.accessCurrentValue();
+      this.state.empty = !inputVal.length;
+      this.state.valid = this.getInputValidity();
+      this.renderValidity();
+    }
+
+    renderValid() {
+      return this.renderClear();
+    }
+
+    renderEmpty() {
+      return this.renderInvalid();
+    }
+
+    renderInvalid() {
+      this.inputElement.addClass(inputErrorClass);
+      this.scopedSiblings.hide();
+      return this.fieldErrorElement.show();
+    }
+
+    renderClear() {
+      const inputVal = this.accessCurrentValue();
+      if (!inputVal.split(' ').length) {
+        const trimmedInput = inputVal.trim();
+        this.accessCurrentValue(trimmedInput);
+      }
+      this.inputElement.removeClass(inputErrorClass);
+      this.scopedSiblings.hide();
+      this.fieldErrorElement.hide();
+    }
+  }
+
+  const customValidationFlag = 'no-gl-field-errors';
+
+  class GlFieldErrors {
+    constructor(form) {
+      this.form = $(form);
+      this.state = {
+        inputs: [],
+        valid: false
+      };
+      this.initValidators();
+    }
+
+    initValidators () {
+      // select all non-hidden inputs in form
+      this.state.inputs = this.form.find(':input:not([type=hidden])').toArray()
+        .filter((input) => !input.classList.contains(customValidationFlag))
+        .map((input) => new GlFieldError({ input, formErrors: this }));
+
+      this.form.on('submit', this.catchInvalidFormSubmit);
+    }
+
+    /* Neccessary to prevent intercept and override invalid form submit
+     * because Safari & iOS quietly allow form submission when form is invalid
+     * and prevents disabling of invalid submit button by application.js */
+
+    catchInvalidFormSubmit (event) {
+      if (!event.currentTarget.checkValidity()) {
+        event.preventDefault();
+        event.stopPropagation();
+      }
+    }
+
+    focusOnFirstInvalid () {
+      const firstInvalid = this.state.inputs.filter((input) => !input.inputDomElement.validity.valid)[0];
+      firstInvalid.inputElement.focus();
+    }
+  }
+
+  global.GlFieldErrors = GlFieldErrors;
+
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/groups.js b/app/assets/javascripts/groups.js
deleted file mode 100644
index 4382dd6860f542d75fdb69d94adca8e82397ed04..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/groups.js
+++ /dev/null
@@ -1,13 +0,0 @@
-(function() {
-  this.GroupMembers = (function() {
-    function GroupMembers() {
-      $('li.group_member').bind('ajax:success', function() {
-        return $(this).fadeOut();
-      });
-    }
-
-    return GroupMembers;
-
-  })();
-
-}).call(this);
diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js
index 1935af491f713d3c3d613471285b4068eb11cd8d..e1532fd9ec426fe86b87fc2b5a92d24e27b5109b 100644
--- a/app/assets/javascripts/member_expiration_date.js
+++ b/app/assets/javascripts/member_expiration_date.js
@@ -14,14 +14,18 @@
     inputs.datepicker({
       dateFormat: 'yy-mm-dd',
       minDate: 1,
-      onSelect: toggleClearInput
+      onSelect: function () {
+        $(this).trigger('change');
+        toggleClearInput.call(this);
+      }
     });
 
     inputs.next('.js-clear-input').on('click', function(event) {
       event.preventDefault();
 
       var input = $(this).closest('.clearable-input').find('.js-access-expiration-date');
-      input.datepicker('setDate', null);
+      input.datepicker('setDate', null)
+        .trigger('change');
       toggleClearInput.call(input);
     });
 
diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6
new file mode 100644
index 0000000000000000000000000000000000000000..a0cd20f21e801535dad610c58e5ed514156ea6d9
--- /dev/null
+++ b/app/assets/javascripts/members.js.es6
@@ -0,0 +1,36 @@
+((w) => {
+  w.gl = w.gl || {};
+
+  class Members {
+    constructor() {
+      this.addListeners();
+    }
+
+    addListeners() {
+      $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow);
+      $('.js-member-update-control').off('change').on('change', this.formSubmit);
+      $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess);
+    }
+
+    removeRow(e) {
+      const $target = $(e.target);
+
+      if ($target.hasClass('btn-remove')) {
+        $target.closest('.member')
+          .fadeOut(function () {
+            $(this).remove();
+          });
+      }
+    }
+
+    formSubmit() {
+      $(this).closest('form').trigger("submit.rails").end().disable();
+    }
+
+    formSuccess() {
+      $(this).find('.js-member-update-control').enable();
+    }
+  }
+
+  gl.Members = Members;
+})(window);
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 8045d24a1bba00491b77b9626b5b187c625e8b06..fd21aa1fefa5a710bd788759d4e9c4e22d2a3f14 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -71,6 +71,7 @@
       this._location = location;
       this.bindEvents();
       this.activateTab(this.opts.action);
+      this.initAffix();
     }
 
     MergeRequestTabs.prototype.bindEvents = function() {
@@ -380,6 +381,46 @@
     // Only when sidebar is collapsed
     };
 
+    MergeRequestTabs.prototype.initAffix = function () {
+      var $tabs = $('.js-tabs-affix');
+
+      // Screen space on small screens is usually very sparse
+      // So we dont affix the tabs on these
+      if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
+
+      var tabsWidth = $tabs.outerWidth(),
+        $diffTabs = $('#diff-notes-app'),
+        offsetTop = $tabs.offset().top - ($('.navbar-fixed-top').height() + $('.layout-nav').height());
+
+      $tabs.off('affix.bs.affix affix-top.bs.affix')
+        .affix({
+          offset: {
+            top: offsetTop
+          }
+        }).on('affix.bs.affix', function () {
+          $tabs.css({
+            left: $tabs.offset().left,
+            width: tabsWidth
+          });
+          $diffTabs.css({
+            marginTop: $tabs.height()
+          });
+        }).on('affix-top.bs.affix', function () {
+          $tabs.css({
+            left: '',
+            width: ''
+          });
+          $diffTabs.css({
+            marginTop: ''
+          });
+        });
+
+      // Fix bug when reloading the page already scrolling
+      if ($tabs.hasClass('affix')) {
+        $tabs.trigger('affix.bs.affix');
+      }
+    };
+
     return MergeRequestTabs;
 
   })();
diff --git a/app/assets/javascripts/merge_request_widget.js b/app/assets/javascripts/merge_request_widget.js.es6
similarity index 68%
rename from app/assets/javascripts/merge_request_widget.js
rename to app/assets/javascripts/merge_request_widget.js.es6
index 7bbcdf5983880534e8362abdf65ec7e327b6fcd2..fcadc4bc515291742c4d98de07e8754f41d7ad16 100644
--- a/app/assets/javascripts/merge_request_widget.js
+++ b/app/assets/javascripts/merge_request_widget.js.es6
@@ -1,7 +1,26 @@
-(function() {
+ ((global) => {
   var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
 
-  this.MergeRequestWidget = (function() {
+  const DEPLOYMENT_TEMPLATE = `<div class="mr-widget-heading" id="<%- id %>">
+       <div class="ci_widget ci-success">
+         <%= ci_success_icon %>
+         <span>
+           Deployed to
+           <a href="<%- url %>" target="_blank" class="environment">
+             <%- name %>
+           </a>
+           <span class="js-environment-timeago" data-toggle="tooltip" data-placement="top" data-title="<%- deployed_at_formatted %>">
+             <%- deployed_at %>
+           </span>
+           <a class="js-environment-link" href="<%- external_url %>" target="_blank">
+             <i class="fa fa-external-link"></i>
+             View on <%- external_url_formatted %>
+           </a>
+         </span>
+       </div>
+     </div>`;
+
+   global.MergeRequestWidget = (function() {
     function MergeRequestWidget(opts) {
       // Initialize MergeRequestWidget behavior
       //
@@ -10,17 +29,23 @@
       //   ci_status_url        - String, URL to use to check CI status
       //
       this.opts = opts;
+      this.$widgetBody = $('.mr-widget-body');
       $('#modal_merge_info').modal({
         show: false
       });
       this.firstCICheck = true;
       this.readyForCICheck = false;
+      this.readyForCIEnvironmentCheck = false;
       this.cancel = false;
       clearInterval(this.fetchBuildStatusInterval);
+      clearInterval(this.fetchBuildEnvironmentStatusInterval);
       this.clearEventListeners();
       this.addEventListeners();
       this.getCIStatus(false);
+      this.getCIEnvironmentsStatus();
+      this.retrieveSuccessIcon();
       this.pollCIStatus();
+      this.pollCIEnvironmentsStatus();
       notifyPermissions();
     }
 
@@ -41,6 +66,7 @@
           page = $('body').data('page').split(':').last();
           if (allowedPages.indexOf(page) < 0) {
             clearInterval(_this.fetchBuildStatusInterval);
+            clearInterval(_this.fetchBuildEnvironmentStatusInterval);
             _this.cancelPolling();
             return _this.clearEventListeners();
           }
@@ -48,6 +74,12 @@
       })(this));
     };
 
+    MergeRequestWidget.prototype.retrieveSuccessIcon = function() {
+       const $ciSuccessIcon = $('.js-success-icon');
+       this.$ciSuccessIcon = $ciSuccessIcon.html();
+       $ciSuccessIcon.remove();
+     }
+
     MergeRequestWidget.prototype.mergeInProgress = function(deleteSourceBranch) {
       if (deleteSourceBranch == null) {
         deleteSourceBranch = false;
@@ -62,7 +94,7 @@
               urlSuffix = deleteSourceBranch ? '?deleted_source_branch=true' : '';
               return window.location.href = window.location.pathname + urlSuffix;
             } else if (data.merge_error) {
-              return $('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>");
+              return this.$widgetBody.html("<h4>" + data.merge_error + "</h4>");
             } else {
               callback = function() {
                 return merge_request_widget.mergeInProgress(deleteSourceBranch);
@@ -118,6 +150,7 @@
           if (data.status === '') {
             return;
           }
+          if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
           if (_this.firstCICheck || data.status !== _this.opts.ci_status && (data.status != null)) {
             _this.opts.ci_status = data.status;
             _this.showCIStatus(data.status);
@@ -150,6 +183,41 @@
       })(this));
     };
 
+    MergeRequestWidget.prototype.pollCIEnvironmentsStatus = function() {
+      this.fetchBuildEnvironmentStatusInterval = setInterval(() => {
+        if (!this.readyForCIEnvironmentCheck) return;
+        this.getCIEnvironmentsStatus();
+        this.readyForCIEnvironmentCheck = false;
+      }, 300000);
+    };
+
+    MergeRequestWidget.prototype.getCIEnvironmentsStatus = function() {
+      $.getJSON(this.opts.ci_environments_status_url, (environments) => {
+        if (this.cancel) return;
+        this.readyForCIEnvironmentCheck = true;
+        if (environments && environments.length) this.renderEnvironments(environments);
+      });
+    };
+
+    MergeRequestWidget.prototype.renderEnvironments = function(environments) {
+      for (let i = 0; i < environments.length; i++) {
+        const environment = environments[i];
+        if ($(`.mr-state-widget #${ environment.id }`).length) return;
+        const $template = $(DEPLOYMENT_TEMPLATE);
+        if (!environment.external_url || !environment.external_url_formatted) $('.js-environment-link', $template).remove();
+        if (environment.deployed_at && environment.deployed_at_formatted) {
+          environment.deployed_at = $.timeago(environment.deployed_at) + '.';
+        } else {
+          $('.js-environment-timeago', $template).remove();
+          environment.name += '.';
+        }
+        environment.ci_success_icon = this.$ciSuccessIcon;
+        const templateString = _.unescape($template[0].outerHTML);
+        const template = _.template(templateString)(environment)
+        this.$widgetBody.before(template);
+      }
+     };
+
     MergeRequestWidget.prototype.showCIStatus = function(state) {
       var allowed_states;
       if (state == null) {
@@ -190,4 +258,4 @@
 
   })();
 
-}).call(this);
+ })(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/pipeline.js.es6 b/app/assets/javascripts/pipelines.js.es6
similarity index 100%
rename from app/assets/javascripts/pipeline.js.es6
rename to app/assets/javascripts/pipelines.js.es6
diff --git a/app/assets/javascripts/project_members.js b/app/assets/javascripts/project_members.js
deleted file mode 100644
index 78f7b48bc7d726d043ea51a09c27bd04c79b17ba..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/project_members.js
+++ /dev/null
@@ -1,10 +0,0 @@
-(function() {
-  this.ProjectMembers = (function() {
-    function ProjectMembers() {
-      $('li.project_member').bind('ajax:success', function() {
-        return $(this).fadeOut();
-      });
-    }
-    return ProjectMembers;
-  })();
-}).call(this);
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js.es6 b/app/assets/javascripts/templates/issuable_template_selector.js.es6
index 2ecf3b189752c9de5da4ea5c91a7b170a37ae6ff..bd4e3c3d00dfb1f62b3fa060d0e5b40b43064a0d 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js.es6
+++ b/app/assets/javascripts/templates/issuable_template_selector.js.es6
@@ -16,7 +16,13 @@
       if (initialQuery.name) this.requestFile(initialQuery);
 
       $('.reset-template', this.dropdown.parent()).on('click', () => {
-        if (this.currentTemplate) this.setInputValueToTemplateContent(false);
+        this.setInputValueToTemplateContent();
+      });
+
+      $('.no-template', this.dropdown.parent()).on('click', () => {
+        this.currentTemplate = '';
+        this.setInputValueToTemplateContent();
+        $('.dropdown-toggle-text', this.dropdown).text('Choose a template');
       });
     }
 
diff --git a/app/assets/javascripts/username_validator.js.es6 b/app/assets/javascripts/username_validator.js.es6
new file mode 100644
index 0000000000000000000000000000000000000000..2517f778365001618aac5bb2176637d573b3fc31
--- /dev/null
+++ b/app/assets/javascripts/username_validator.js.es6
@@ -0,0 +1,133 @@
+((global) => {
+  const debounceTimeoutDuration = 1000;
+  const invalidInputClass = 'gl-field-error-outline';
+  const successInputClass = 'gl-field-success-outline';
+  const unavailableMessageSelector = '.username .validation-error';
+  const successMessageSelector = '.username .validation-success';
+  const pendingMessageSelector = '.username .validation-pending';
+  const invalidMessageSelector = '.username .gl-field-error';
+
+  class UsernameValidator {
+    constructor() {
+      this.inputElement = $('#new_user_username');
+      this.inputDomElement = this.inputElement.get(0);
+      this.state = {
+        available: false,
+        valid: false,
+        pending: false,
+        empty: true
+      };
+
+      const debounceTimeout = _.debounce((username) => {
+        this.validateUsername(username);
+      }, debounceTimeoutDuration);
+
+      this.inputElement.on('keyup.username_check', () => {
+        const username = this.inputElement.val();
+
+        this.state.valid = this.inputDomElement.validity.valid;
+        this.state.empty = !username.length;
+
+        if (this.state.valid) {
+          return debounceTimeout(username);
+        }
+
+        this.renderState();
+      });
+
+      // Override generic field validation
+      this.inputElement.on('invalid', this.interceptInvalid.bind(this));
+    }
+
+    renderState() {
+      // Clear all state
+      this.clearFieldValidationState();
+
+      if (this.state.valid && this.state.available) {
+        return this.setSuccessState();
+      }
+
+      if (this.state.empty) {
+        return this.clearFieldValidationState();
+      }
+
+      if (this.state.pending) {
+        return this.setPendingState();
+      }
+
+      if (!this.state.available) {
+        return this.setUnavailableState();
+      }
+
+      if (!this.state.valid) {
+        return this.setInvalidState();
+      }
+    }
+
+    interceptInvalid(event) {
+      event.preventDefault();
+      event.stopPropagation();
+    }
+
+    validateUsername(username) {
+      if (this.state.valid) {
+        this.state.pending = true;
+        this.state.available = false;
+        this.renderState();
+        return $.ajax({
+          type: 'GET',
+          url: `/u/${username}/exists`,
+          dataType: 'json',
+          success: (res) => this.setAvailabilityState(res.exists)
+        });
+      }
+    }
+
+    setAvailabilityState(usernameTaken) {
+      if (usernameTaken) {
+        this.state.valid = false;
+        this.state.available = false;
+      } else {
+        this.state.available = true;
+      }
+      this.state.pending = false;
+      this.renderState();
+    }
+
+    clearFieldValidationState() {
+      this.inputElement.siblings('p').hide();
+
+      this.inputElement.removeClass(invalidInputClass)
+        .removeClass(successInputClass);
+    }
+
+    setUnavailableState() {
+      const $usernameUnavailableMessage = this.inputElement.siblings(unavailableMessageSelector);
+      this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
+      $usernameUnavailableMessage.show();
+    }
+
+    setSuccessState() {
+      const $usernameSuccessMessage = this.inputElement.siblings(successMessageSelector);
+      this.inputElement.addClass(successInputClass).removeClass(invalidInputClass);
+      $usernameSuccessMessage.show();
+    }
+
+    setPendingState() {
+      const $usernamePendingMessage = $(pendingMessageSelector);
+      if (this.state.pending) {
+        $usernamePendingMessage.show();
+      } else {
+        $usernamePendingMessage.hide();
+      }
+    }
+
+    setInvalidState() {
+      const $inputErrorMessage = $(invalidMessageSelector);
+      this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
+      $inputErrorMessage.show();
+    }
+  }
+
+  global.UsernameValidator = UsernameValidator;
+})(window);
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 6aa0e1cd2b61bbbf00dc4e69485425545197694e..3020b7cc2392e9e0dd383e30338256cd5ddffd61 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -325,6 +325,10 @@
     };
 
     UsersSelect.prototype.user = function(user_id, callback) {
+      if(!/^\d+$/.test(user_id)) {
+        return false;
+      }
+
       var url;
       url = this.buildUrl(this.userPath);
       url = url.replace(':id', user_id);
diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss
index 897bc49e7df0e098b98128d4479d0bd446351dec..e3ca7f6373a147093079223127620d8e08ee1d75 100644
--- a/app/assets/stylesheets/behaviors.scss
+++ b/app/assets/stylesheets/behaviors.scss
@@ -5,6 +5,7 @@
     display: none;
     &.hide { display: block; }
   }
+
   &.open .content {
     display: block;
     &.hide { display: none; }
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 8002e56724b56517b7db41b3b8da13a4ed1479a7..df2e2ea8d2c796e45263c35c4db2567f68aa7c42 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -19,6 +19,7 @@
 
   &.diff-collapsed {
     padding: 5px;
+
     .click-to-expand {
       cursor: pointer;
     }
@@ -203,6 +204,7 @@
       }
     }
   }
+
   &.user-cover-block {
     padding: 24px 0 0;
   }
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index a7c8d782e9b179416f0ff461f1c08dc5c4a239f1..e6656c2d69a2c87087ba7aaaa66148b2a76f007c 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -25,7 +25,7 @@
   &:focus {
     background-color: $hover-background;
     color: $hover-text;
-    border-color: $hover-border;;
+    border-color: $hover-border;
   }
 }
 
@@ -152,7 +152,8 @@
     @include btn-blue-medium;
   }
 
-  &.btn-info {
+  &.btn-info,
+  &.btn-register {
     @include btn-blue;
   }
 
@@ -240,6 +241,7 @@
   width: 100%;
   margin: 0;
   margin-bottom: 15px;
+
   &.btn {
     padding: 6px 0;
   }
@@ -321,6 +323,7 @@
 
 .btn-build {
   margin-left: 10px;
+
   i {
     color: $gl-icon-color;
   }
@@ -328,6 +331,7 @@
 
 .clone-dropdown-btn a {
   color: $dropdown-link-color;
+
   &:hover {
     text-decoration: none;
   }
@@ -337,6 +341,7 @@
   background-color: $background-color !important;
   border: 1px solid lightgrey;
   cursor: default;
+
   &:active {
     -moz-box-shadow: inset 0 0 0 white;
     -webkit-box-shadow: inset 0 0 0 white;
diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss
index da7bab74a32dc1e55f042f8ae51bec05e08232b2..f3b6ad88ad6f7bf084fcfa04efefe10168c465a3 100644
--- a/app/assets/stylesheets/framework/callout.scss
+++ b/app/assets/stylesheets/framework/callout.scss
@@ -13,10 +13,12 @@
   color: $text-color;
   background: $background-color;
 }
+
 .bs-callout h4 {
   margin-top: 0;
   margin-bottom: 5px;
 }
+
 .bs-callout p:last-child {
   margin-bottom: 0;
 }
@@ -27,16 +29,19 @@
   border-color: #eed3d7;
   color: #b94a48;
 }
+
 .bs-callout-warning {
   background-color: #faf8f0;
   border-color: #faebcc;
   color: #8a6d3b;
 }
+
 .bs-callout-info {
   background-color: #f4f8fa;
   border-color: #bce8f1;
   color: #34789a;
 }
+
 .bs-callout-success {
   background-color: #dff0d8;
   border-color: #5ca64d;
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 5957dce89bc397aee16ecec43eebaa6e4e76c7a5..81e4e264560aa1dc83c801b84e2ae82af68681aa 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -1,31 +1,31 @@
 /** COLORS **/
 .cgray { color: $gl-gray; }
-.clgray { color: #bbb }
+.clgray { color: #bbb; }
 .cred { color: $gl-text-red; }
 .cgreen { color: $gl-text-green; }
-.cdark { color: #444 }
+.cdark { color: #444; }
 
 /** COMMON CLASSES **/
 .prepend-top-0 { margin-top: 0; }
 .prepend-top-5 { margin-top: 5px; }
-.prepend-top-10 { margin-top: 10px }
+.prepend-top-10 { margin-top: 10px; }
 .prepend-top-default { margin-top: $gl-padding !important; }
-.prepend-top-20 { margin-top: 20px }
-.prepend-left-5 { margin-left: 5px }
-.prepend-left-10 { margin-left: 10px }
+.prepend-top-20 { margin-top: 20px; }
+.prepend-left-5 { margin-left: 5px; }
+.prepend-left-10 { margin-left: 10px; }
 .prepend-left-default { margin-left: $gl-padding; }
-.prepend-left-20 { margin-left: 20px }
-.append-right-5 { margin-right: 5px }
-.append-right-10 { margin-right: 10px }
+.prepend-left-20 { margin-left: 20px; }
+.append-right-5 { margin-right: 5px; }
+.append-right-10 { margin-right: 10px; }
 .append-right-default { margin-right: $gl-padding; }
-.append-right-20 { margin-right: 20px }
-.append-bottom-0 { margin-bottom: 0 }
-.append-bottom-10 { margin-bottom: 10px }
-.append-bottom-15 { margin-bottom: 15px }
-.append-bottom-20 { margin-bottom: 20px }
+.append-right-20 { margin-right: 20px; }
+.append-bottom-0 { margin-bottom: 0; }
+.append-bottom-10 { margin-bottom: 10px; }
+.append-bottom-15 { margin-bottom: 15px; }
+.append-bottom-20 { margin-bottom: 20px; }
 .append-bottom-default { margin-bottom: $gl-padding; }
-.inline { display: inline-block }
-.center { text-align: center }
+.inline { display: inline-block; }
+.center { text-align: center; }
 
 .underlined-link { text-decoration: underline; }
 .hint { font-style: italic; color: #999; }
@@ -97,6 +97,7 @@ span.update-author {
   color: #999;
   font-weight: normal;
   font-style: italic;
+
   strong {
     font-weight: bold;
     font-style: normal;
@@ -128,7 +129,7 @@ p.time {
 
 // Fix issue with notes & lists creating a bunch of bottom borders.
 li.note {
-  img { max-width: 100% }
+  img { max-width: 100%; }
   .note-title {
     li {
       border-bottom: none !important;
@@ -172,6 +173,7 @@ li.note {
   @extend .col-md-6;
   text-align: left;
   margin-top: 40px;
+
   pre {
     background: white;
     border: none;
@@ -197,6 +199,7 @@ li.note {
   background: #c67;
   color: #fff;
   font-weight: bold;
+
   a {
     color: #fff;
     text-decoration: underline;
@@ -227,6 +230,7 @@ li.note {
   &.milestone-closed {
     background: $gray-light;
   }
+
   .progress {
     margin-bottom: 0;
     margin-top: 4px;
@@ -286,6 +290,7 @@ table {
 
 .footer-links {
   margin-bottom: 20px;
+
   a {
     margin-right: 15px;
   }
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index baa95711329c5af1e8e16fba4c46f95a585bee75..a839371a6f2d7ccef41917fe721afa364c1830db 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -12,6 +12,7 @@
   .dropdown-menu,
   .dropdown-menu-nav {
     display: block;
+
     @media (max-width: $screen-xs-max) {
       width: 100%;
     }
@@ -48,6 +49,7 @@
     margin-top: -6px;
     color: $dropdown-toggle-icon-color;
     font-size: 10px;
+
     &.fa-spinner {
       font-size: 16px;
       margin-top: -8px;
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 815205005944cd929333fb034483e69d79be2622..13c1bbf0359819c30845961efe4403807060e282 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -26,15 +26,6 @@
     padding: 10px $gl-padding;
     word-wrap: break-word;
     border-radius: 3px 3px 0 0;
-    cursor: pointer;
-
-    &:hover {
-      background-color: $dark-background-color;
-    }
-
-    .diff-toggle-caret {
-      padding-right: 6px; 
-    }
 
     &.file-title-clear {
       padding-left: 0;
@@ -66,6 +57,7 @@
       margin-top: -3px;
     }
   }
+
   .file-content {
     background: #fff;
 
@@ -105,22 +97,27 @@
         border: none;
         margin: 0;
       }
+
       tr {
         border-bottom: 1px solid #eee;
       }
+
       td {
         &:first-child {
           border-left: none;
         }
+
         &:last-child {
           border-right: none;
         }
       }
+
       td.blame-commit {
         padding: 0 10px;
         min-width: 400px;
         background: $gray-light;
       }
+
       td.line-numbers {
         float: none;
         border-left: 1px solid #ddd;
@@ -130,6 +127,7 @@
           margin-right: 0;
         }
       }
+
       td.lines {
         padding: 0;
       }
@@ -146,8 +144,10 @@
         border-left: 1px solid $border-color;
         margin-bottom: 0;
         background: white;
+
         li {
           color: #888;
+
           p {
             margin: 0;
             color: #333;
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 05e8ee0190d8aeb1705b539f988de8b8847187ae..761c07384f472975ccc2776760640234c3ff559a 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -9,7 +9,7 @@ input {
 input[type='text'].danger {
   background: #f2dede!important;
   border-color: #d66;
-  text-shadow: 0 1px 1px #fff
+  text-shadow: 0 1px 1px #fff;
 }
 
 .datetime-controls {
@@ -73,8 +73,8 @@ label {
 }
 
 .form-control {
-  box-shadow: none;
-  border-radius: 3px;
+  @include box-shadow(none);
+  border-radius: 2px;
   padding: $gl-vert-padding $gl-input-padding;
 }
 
@@ -117,9 +117,11 @@ label {
     display: table-cell;
     width: 200px !important;
   }
+
   .input-group-addon {
     background-color: #f7f8fa;
   }
+
   .input-group-addon:not(:first-child):not(:last-child) {
     border-left: 0;
     border-right: 0;
@@ -129,3 +131,8 @@ label {
 .help-block {
   margin-bottom: 0;
 }
+
+.gl-field-error {
+  color: $red-normal;
+}
+
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 3673b81f183ff0e34ea288bb34eaa4319841e104..fe834f4e2f63e0fdcfa8e862606f7f5e23da62fd 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -62,7 +62,7 @@
         }
 
         i {
-          color: $white-light
+          color: $white-light;
         }
 
         path,
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 9823abdde1f1d68f29c7165324309175d5952564..3a4fdd0da22ac75b18a63377131d57b5213cc3d4 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -168,6 +168,7 @@ header {
 
       a {
         color: $gl-text-color;
+
         &:hover {
           text-decoration: underline;
         }
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index efc348214c264d5b89601111eb91e728063e2f81..4b2627c1b870e47b88e26109621ffc2a44c862f2 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -60,6 +60,7 @@
       padding-top: 1px;
       margin: 0;
       color: $gray-dark;
+
       img {
         position: relative;
         top: 3px;
@@ -128,6 +129,10 @@ ul.content-list {
       color: $gl-dark-link-color;
     }
 
+    .member-group-link {
+      color: $blue-normal;
+    }
+
     .description {
       p {
         @include str-truncated;
@@ -168,6 +173,14 @@ ul.content-list {
       }
     }
 
+    .member-controls {
+      float: none;
+
+      @media (min-width: $screen-sm-min) {
+        float: right;
+      }
+    }
+
     // When dragging a list item
     &.ui-sortable-helper {
       border-bottom: none;
diff --git a/app/assets/stylesheets/framework/logo.scss b/app/assets/stylesheets/framework/logo.scss
index c214eabcad7215a306a68fb43308ee39b818062e..a90e45bb5f4a6cc260ab1aa4f3e4fafa6f25a3cc 100644
--- a/app/assets/stylesheets/framework/logo.scss
+++ b/app/assets/stylesheets/framework/logo.scss
@@ -37,6 +37,7 @@
         0%, 10%, 100% {
           fill: lighten($tanuki-yellow, 25%);
         }
+
         90% {
           fill: $tanuki-yellow;
         }
@@ -48,6 +49,7 @@
         10%, 80% {
           fill: $tanuki-orange;
         }
+
         20%, 90% {
           fill: lighten($tanuki-orange, 25%);
         }
@@ -59,6 +61,7 @@
         10%, 80% {
           fill: $tanuki-red;
         }
+ 
         20%, 90% {
           fill: lighten($tanuki-red, 25%);
         }
@@ -70,6 +73,7 @@
         20%, 70% {
           fill: $tanuki-red;
         }
+
         30%, 80% {
           fill: lighten($tanuki-red, 25%);
         }
@@ -81,6 +85,7 @@
         30%, 60% {
           fill: $tanuki-orange;
         }
+
         40%, 70% {
           fill: lighten($tanuki-orange, 25%);
         }
@@ -92,6 +97,7 @@
         30%, 60% {
           fill: $tanuki-red;
         }
+
         40%, 70% {
           fill: lighten($tanuki-red, 25%);
         }
@@ -103,6 +109,7 @@
         40% {
           fill: $tanuki-yellow;
         }
+
         60% {
           fill: lighten($tanuki-yellow, 25%);
         }
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 7fabf27a558164cb8e72e88a4bb3201e58311f20..f84ca36d10f5d797640497b1c5e2f77db071504b 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -34,6 +34,7 @@
 
     &.active {
       background: $gray-light;
+
       a {
         font-weight: 600;
       }
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index ea43f4afc374ba176dc1ee40c838087df23dd35c..899db045b7476510b46da9143dccf44e6f94b7fd 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -210,6 +210,7 @@
     @media (max-width: $screen-xs-max) {
       padding-bottom: 0;
       width: 100%;
+
       .btn, form, .dropdown, .dropdown-menu-toggle, .form-control {
         margin: 0 0 10px;
         display: block;
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index c6f30e144fdcedfe82350df44c72cda2b6404b86..5ba0486177fa49bfbbe3053cf2e4cc748ddf692e 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -13,6 +13,11 @@
     .dropdown-menu-toggle {
       line-height: 20px;
     }
+
+    .badge {
+      margin-top: -2px;
+      margin-left: 5px;
+    }
   }
 
   .panel-body {
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index 79cd26714a3f75b1fa06c7783798dc016488716e..e0708c65695235d625b6474bec55ae7fa8bfbcc3 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -93,7 +93,7 @@
     background: none;
 
     .select2-search-field input {
-      padding: $gl-padding / 2;
+      padding: 5px $gl-padding / 2;
       font-size: 13px;
       height: auto;
       font-family: inherit;
@@ -101,7 +101,7 @@
     }
 
     .select2-search-choice {
-      margin: 8px 0 0 8px;
+      margin: 5px 0 0 8px;
       box-shadow: none;
       border-color: $input-border;
       color: $gl-text-color;
@@ -137,6 +137,7 @@
 
   .select2-results {
     max-height: 350px;
+
     .select2-highlighted {
       background: $gl-primary;
     }
@@ -212,9 +213,11 @@
   .group-image {
     float: left;
   }
+
   .group-name {
     font-weight: bold;
   }
+
   .group-path {
     color: #999;
   }
@@ -239,6 +242,7 @@
     color: #aaa;
     font-weight: normal;
   }
+
   .namespace-path {
     margin-left: 10px;
     font-weight: bolder;
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index 0b0bd80c3269e554fd9d5f8b4bb31e6dfb4f6ce8..eb63a9f214b7d8db98b95c0121eed4babfe24db6 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -48,6 +48,7 @@
     &:before {
       background: none;
     }
+
     .timeline-entry .timeline-entry-inner {
       .timeline-icon {
         display: none;
diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index e3154657c5416a4d37cbda04017decae406bc629..f410664126922b1eb0dfa06ea5f4c554e4e1f3cc 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -48,31 +48,40 @@
 .clearfix {
   @include clearfix();
 }
+
 .center-block {
   @include center-block();
 }
+
 .pull-right {
   float: right !important;
 }
+
 .pull-left {
   float: left !important;
 }
+
 .hide {
   display: none;
 }
+
 .show {
   display: block !important;
 }
+
 .invisible {
   visibility: hidden;
 }
+
 .text-hide {
   @include text-hide();
 }
+
 .hidden {
   display: none !important;
   visibility: hidden !important;
 }
+
 .affix {
   position: fixed;
 }
@@ -146,6 +155,7 @@
       padding: 6px 15px;
       font-size: 13px;
       font-weight: normal;
+
       a {
         color: #777;
       }
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index d099a884f545ab622ca4fe1980f7c4ac62b1dc0b..8df0067fac175ce45f751f44f1d1ab42b0946307 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -106,6 +106,7 @@
     @extend .table-bordered;
     margin: 12px 0;
     color: #5c5d5e;
+
     th {
       background: #f8fafc;
     }
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index 16ffbe57a99fbe93f6587700468f0e9ab983ea33..a3acee299e3a258a1b4e8728aa5a219c06698c31 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -55,68 +55,68 @@
     color: #000 !important;
   }
 
-  .hll { background-color: #373b41 }
-  .c { color: #969896 } /* Comment */
-  .err { color: #c66 } /* Error */
-  .k { color: #b294bb } /* Keyword */
-  .l { color: #de935f } /* Literal */
-  .n { color: #c5c8c6 } /* Name */
-  .o { color: #8abeb7 } /* Operator */
-  .p { color: #c5c8c6 } /* Punctuation */
-  .cm { color: #969896 } /* Comment.Multiline */
-  .cp { color: #969896 } /* Comment.Preproc */
-  .c1 { color: #969896 } /* Comment.Single */
-  .cs { color: #969896 } /* Comment.Special */
-  .gd { color: #c66 } /* Generic.Deleted */
-  .ge { font-style: italic } /* Generic.Emph */
-  .gh { color: #c5c8c6; font-weight: bold } /* Generic.Heading */
-  .gi { color: #b5bd68 } /* Generic.Inserted */
-  .gp { color: #969896; font-weight: bold } /* Generic.Prompt */
-  .gs { font-weight: bold } /* Generic.Strong */
-  .gu { color: #8abeb7; font-weight: bold } /* Generic.Subheading */
-  .kc { color: #b294bb } /* Keyword.Constant */
-  .kd { color: #b294bb } /* Keyword.Declaration */
-  .kn { color: #8abeb7 } /* Keyword.Namespace */
-  .kp { color: #b294bb } /* Keyword.Pseudo */
-  .kr { color: #b294bb } /* Keyword.Reserved */
-  .kt { color: #f0c674 } /* Keyword.Type */
-  .ld { color: #b5bd68 } /* Literal.Date */
-  .m { color: #de935f } /* Literal.Number */
-  .s { color: #b5bd68 } /* Literal.String */
-  .na { color: #81a2be } /* Name.Attribute */
-  .nb { color: #c5c8c6 } /* Name.Builtin */
-  .nc { color: #f0c674 } /* Name.Class */
-  .no { color: #c66 } /* Name.Constant */
-  .nd { color: #8abeb7 } /* Name.Decorator */
-  .ni { color: #c5c8c6 } /* Name.Entity */
-  .ne { color: #c66 } /* Name.Exception */
-  .nf { color: #81a2be } /* Name.Function */
-  .nl { color: #c5c8c6 } /* Name.Label */
-  .nn { color: #f0c674 } /* Name.Namespace */
-  .nx { color: #81a2be } /* Name.Other */
-  .py { color: #c5c8c6 } /* Name.Property */
-  .nt { color: #8abeb7 } /* Name.Tag */
-  .nv { color: #c66 } /* Name.Variable */
-  .ow { color: #8abeb7 } /* Operator.Word */
-  .w { color: #c5c8c6 } /* Text.Whitespace */
-  .mf { color: #de935f } /* Literal.Number.Float */
-  .mh { color: #de935f } /* Literal.Number.Hex */
-  .mi { color: #de935f } /* Literal.Number.Integer */
-  .mo { color: #de935f } /* Literal.Number.Oct */
-  .sb { color: #b5bd68 } /* Literal.String.Backtick */
-  .sc { color: #c5c8c6 } /* Literal.String.Char */
-  .sd { color: #969896 } /* Literal.String.Doc */
-  .s2 { color: #b5bd68 } /* Literal.String.Double */
-  .se { color: #de935f } /* Literal.String.Escape */
-  .sh { color: #b5bd68 } /* Literal.String.Heredoc */
-  .si { color: #de935f } /* Literal.String.Interpol */
-  .sx { color: #b5bd68 } /* Literal.String.Other */
-  .sr { color: #b5bd68 } /* Literal.String.Regex */
-  .s1 { color: #b5bd68 } /* Literal.String.Single */
-  .ss { color: #b5bd68 } /* Literal.String.Symbol */
-  .bp { color: #c5c8c6 } /* Name.Builtin.Pseudo */
-  .vc { color: #c66 } /* Name.Variable.Class */
-  .vg { color: #c66 } /* Name.Variable.Global */
-  .vi { color: #c66 } /* Name.Variable.Instance */
-  .il { color: #de935f } /* Literal.Number.Integer.Long */
+  .hll { background-color: #373b41; }
+  .c { color: #969896; } /* Comment */
+  .err { color: #c66; } /* Error */
+  .k { color: #b294bb; } /* Keyword */
+  .l { color: #de935f; } /* Literal */
+  .n { color: #c5c8c6; } /* Name */
+  .o { color: #8abeb7; } /* Operator */
+  .p { color: #c5c8c6; } /* Punctuation */
+  .cm { color: #969896; } /* Comment.Multiline */
+  .cp { color: #969896; } /* Comment.Preproc */
+  .c1 { color: #969896; } /* Comment.Single */
+  .cs { color: #969896; } /* Comment.Special */
+  .gd { color: #c66; } /* Generic.Deleted */
+  .ge { font-style: italic; } /* Generic.Emph */
+  .gh { color: #c5c8c6; font-weight: bold; } /* Generic.Heading */
+  .gi { color: #b5bd68; } /* Generic.Inserted */
+  .gp { color: #969896; font-weight: bold; } /* Generic.Prompt */
+  .gs { font-weight: bold; } /* Generic.Strong */
+  .gu { color: #8abeb7; font-weight: bold; } /* Generic.Subheading */
+  .kc { color: #b294bb; } /* Keyword.Constant */
+  .kd { color: #b294bb; } /* Keyword.Declaration */
+  .kn { color: #8abeb7; } /* Keyword.Namespace */
+  .kp { color: #b294bb; } /* Keyword.Pseudo */
+  .kr { color: #b294bb; } /* Keyword.Reserved */
+  .kt { color: #f0c674; } /* Keyword.Type */
+  .ld { color: #b5bd68; } /* Literal.Date */
+  .m { color: #de935f; } /* Literal.Number */
+  .s { color: #b5bd68; } /* Literal.String */
+  .na { color: #81a2be; } /* Name.Attribute */
+  .nb { color: #c5c8c6; } /* Name.Builtin */
+  .nc { color: #f0c674; } /* Name.Class */
+  .no { color: #c66; } /* Name.Constant */
+  .nd { color: #8abeb7; } /* Name.Decorator */
+  .ni { color: #c5c8c6; } /* Name.Entity */
+  .ne { color: #c66; } /* Name.Exception */
+  .nf { color: #81a2be; } /* Name.Function */
+  .nl { color: #c5c8c6; } /* Name.Label */
+  .nn { color: #f0c674; } /* Name.Namespace */
+  .nx { color: #81a2be; } /* Name.Other */
+  .py { color: #c5c8c6; } /* Name.Property */
+  .nt { color: #8abeb7; } /* Name.Tag */
+  .nv { color: #c66; } /* Name.Variable */
+  .ow { color: #8abeb7; } /* Operator.Word */
+  .w { color: #c5c8c6; } /* Text.Whitespace */
+  .mf { color: #de935f; } /* Literal.Number.Float */
+  .mh { color: #de935f; } /* Literal.Number.Hex */
+  .mi { color: #de935f; } /* Literal.Number.Integer */
+  .mo { color: #de935f; } /* Literal.Number.Oct */
+  .sb { color: #b5bd68; } /* Literal.String.Backtick */
+  .sc { color: #c5c8c6; } /* Literal.String.Char */
+  .sd { color: #969896; } /* Literal.String.Doc */
+  .s2 { color: #b5bd68; } /* Literal.String.Double */
+  .se { color: #de935f; } /* Literal.String.Escape */
+  .sh { color: #b5bd68; } /* Literal.String.Heredoc */
+  .si { color: #de935f; } /* Literal.String.Interpol */
+  .sx { color: #b5bd68; } /* Literal.String.Other */
+  .sr { color: #b5bd68; } /* Literal.String.Regex */
+  .s1 { color: #b5bd68; } /* Literal.String.Single */
+  .ss { color: #b5bd68; } /* Literal.String.Symbol */
+  .bp { color: #c5c8c6; } /* Name.Builtin.Pseudo */
+  .vc { color: #c66; } /* Name.Variable.Class */
+  .vg { color: #c66; } /* Name.Variable.Global */
+  .vi { color: #c66; } /* Name.Variable.Instance */
+  .il { color: #de935f; } /* Literal.Number.Integer.Long */
 }
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index 7de920e074b37a01aeaa519ae3a3e3701167ec37..e9228c94db99524ed1c451944fe91d56862aa3aa 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -55,65 +55,65 @@
     color: #000 !important;
   }
 
-  .hll { background-color: #49483e }
-  .c { color: #75715e } /* Comment */
-  .err { color: #960050; background-color: #1e0010 } /* Error */
-  .k { color: #66d9ef } /* Keyword */
-  .l { color: #ae81ff } /* Literal */
-  .n { color: #f8f8f2 } /* Name */
-  .o { color: #f92672 } /* Operator */
-  .p { color: #f8f8f2 } /* Punctuation */
-  .cm { color: #75715e } /* Comment.Multiline */
-  .cp { color: #75715e } /* Comment.Preproc */
-  .c1 { color: #75715e } /* Comment.Single */
-  .cs { color: #75715e } /* Comment.Special */
-  .ge { font-style: italic } /* Generic.Emph */
-  .gs { font-weight: bold } /* Generic.Strong */
-  .kc { color: #66d9ef } /* Keyword.Constant */
-  .kd { color: #66d9ef } /* Keyword.Declaration */
-  .kn { color: #f92672 } /* Keyword.Namespace */
-  .kp { color: #66d9ef } /* Keyword.Pseudo */
-  .kr { color: #66d9ef } /* Keyword.Reserved */
-  .kt { color: #66d9ef } /* Keyword.Type */
-  .ld { color: #e6db74 } /* Literal.Date */
-  .m { color: #ae81ff } /* Literal.Number */
-  .s { color: #e6db74 } /* Literal.String */
-  .na { color: #a6e22e } /* Name.Attribute */
-  .nb { color: #f8f8f2 } /* Name.Builtin */
-  .nc { color: #a6e22e } /* Name.Class */
-  .no { color: #66d9ef } /* Name.Constant */
-  .nd { color: #a6e22e } /* Name.Decorator */
-  .ni { color: #f8f8f2 } /* Name.Entity */
-  .ne { color: #a6e22e } /* Name.Exception */
-  .nf { color: #a6e22e } /* Name.Function */
-  .nl { color: #f8f8f2 } /* Name.Label */
-  .nn { color: #f8f8f2 } /* Name.Namespace */
-  .nx { color: #a6e22e } /* Name.Other */
-  .py { color: #f8f8f2 } /* Name.Property */
-  .nt { color: #f92672 } /* Name.Tag */
-  .nv { color: #f8f8f2 } /* Name.Variable */
-  .ow { color: #f92672 } /* Operator.Word */
-  .w { color: #f8f8f2 } /* Text.Whitespace */
-  .mf { color: #ae81ff } /* Literal.Number.Float */
-  .mh { color: #ae81ff } /* Literal.Number.Hex */
-  .mi { color: #ae81ff } /* Literal.Number.Integer */
-  .mo { color: #ae81ff } /* Literal.Number.Oct */
-  .sb { color: #e6db74 } /* Literal.String.Backtick */
-  .sc { color: #e6db74 } /* Literal.String.Char */
-  .sd { color: #e6db74 } /* Literal.String.Doc */
-  .s2 { color: #e6db74 } /* Literal.String.Double */
-  .se { color: #ae81ff } /* Literal.String.Escape */
-  .sh { color: #e6db74 } /* Literal.String.Heredoc */
-  .si { color: #e6db74 } /* Literal.String.Interpol */
-  .sx { color: #e6db74 } /* Literal.String.Other */
-  .sr { color: #e6db74 } /* Literal.String.Regex */
-  .s1 { color: #e6db74 } /* Literal.String.Single */
-  .ss { color: #e6db74 } /* Literal.String.Symbol */
-  .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
-  .vc { color: #f8f8f2 } /* Name.Variable.Class */
-  .vg { color: #f8f8f2 } /* Name.Variable.Global */
-  .vi { color: #f8f8f2 } /* Name.Variable.Instance */
-  .il { color: #ae81ff } /* Literal.Number.Integer.Long */
+  .hll { background-color: #49483e; }
+  .c { color: #75715e; } /* Comment */
+  .err { color: #960050; background-color: #1e0010; } /* Error */
+  .k { color: #66d9ef; } /* Keyword */
+  .l { color: #ae81ff; } /* Literal */
+  .n { color: #f8f8f2; } /* Name */
+  .o { color: #f92672; } /* Operator */
+  .p { color: #f8f8f2; } /* Punctuation */
+  .cm { color: #75715e; } /* Comment.Multiline */
+  .cp { color: #75715e; } /* Comment.Preproc */
+  .c1 { color: #75715e; } /* Comment.Single */
+  .cs { color: #75715e; } /* Comment.Special */
+  .ge { font-style: italic; } /* Generic.Emph */
+  .gs { font-weight: bold; } /* Generic.Strong */
+  .kc { color: #66d9ef; } /* Keyword.Constant */
+  .kd { color: #66d9ef; } /* Keyword.Declaration */
+  .kn { color: #f92672; } /* Keyword.Namespace */
+  .kp { color: #66d9ef; } /* Keyword.Pseudo */
+  .kr { color: #66d9ef; } /* Keyword.Reserved */
+  .kt { color: #66d9ef; } /* Keyword.Type */
+  .ld { color: #e6db74; } /* Literal.Date */
+  .m { color: #ae81ff; } /* Literal.Number */
+  .s { color: #e6db74; } /* Literal.String */
+  .na { color: #a6e22e; } /* Name.Attribute */
+  .nb { color: #f8f8f2; } /* Name.Builtin */
+  .nc { color: #a6e22e; } /* Name.Class */
+  .no { color: #66d9ef; } /* Name.Constant */
+  .nd { color: #a6e22e; } /* Name.Decorator */
+  .ni { color: #f8f8f2; } /* Name.Entity */
+  .ne { color: #a6e22e; } /* Name.Exception */
+  .nf { color: #a6e22e; } /* Name.Function */
+  .nl { color: #f8f8f2; } /* Name.Label */
+  .nn { color: #f8f8f2; } /* Name.Namespace */
+  .nx { color: #a6e22e; } /* Name.Other */
+  .py { color: #f8f8f2; } /* Name.Property */
+  .nt { color: #f92672; } /* Name.Tag */
+  .nv { color: #f8f8f2; } /* Name.Variable */
+  .ow { color: #f92672; } /* Operator.Word */
+  .w { color: #f8f8f2; } /* Text.Whitespace */
+  .mf { color: #ae81ff; } /* Literal.Number.Float */
+  .mh { color: #ae81ff; } /* Literal.Number.Hex */
+  .mi { color: #ae81ff; } /* Literal.Number.Integer */
+  .mo { color: #ae81ff; } /* Literal.Number.Oct */
+  .sb { color: #e6db74; } /* Literal.String.Backtick */
+  .sc { color: #e6db74; } /* Literal.String.Char */
+  .sd { color: #e6db74; } /* Literal.String.Doc */
+  .s2 { color: #e6db74; } /* Literal.String.Double */
+  .se { color: #ae81ff; } /* Literal.String.Escape */
+  .sh { color: #e6db74; } /* Literal.String.Heredoc */
+  .si { color: #e6db74; } /* Literal.String.Interpol */
+  .sx { color: #e6db74; } /* Literal.String.Other */
+  .sr { color: #e6db74; } /* Literal.String.Regex */
+  .s1 { color: #e6db74; } /* Literal.String.Single */
+  .ss { color: #e6db74; } /* Literal.String.Symbol */
+  .bp { color: #f8f8f2; } /* Name.Builtin.Pseudo */
+  .vc { color: #f8f8f2; } /* Name.Variable.Class */
+  .vg { color: #f8f8f2; } /* Name.Variable.Global */
+  .vi { color: #f8f8f2; } /* Name.Variable.Instance */
+  .il { color: #ae81ff; } /* Literal.Number.Integer.Long */
   .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */
   .gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */
   .gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index b11499c71eec8e065e899011e0143143c704cfbf..c3c7773b9e2b4d25d2a6f303136600e64ecfe00e 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -72,72 +72,72 @@
   green     #859900  operators, other keywords
   */
 
-  .c { color: #586e75 } /* Comment */
-  .err { color: #93a1a1 } /* Error */
-  .g { color: #93a1a1 } /* Generic */
-  .k { color: #859900 } /* Keyword */
-  .l { color: #93a1a1 } /* Literal */
-  .n { color: #93a1a1 } /* Name */
-  .o { color: #859900 } /* Operator */
-  .x { color: #cb4b16 } /* Other */
-  .p { color: #93a1a1 } /* Punctuation */
-  .cm { color: #586e75 } /* Comment.Multiline */
-  .cp { color: #859900 } /* Comment.Preproc */
-  .c1 { color: #586e75 } /* Comment.Single */
-  .cs { color: #859900 } /* Comment.Special */
-  .gd { color: #2aa198 } /* Generic.Deleted */
-  .ge { color: #93a1a1; font-style: italic } /* Generic.Emph */
-  .gr { color: #dc322f } /* Generic.Error */
-  .gh { color: #cb4b16 } /* Generic.Heading */
-  .gi { color: #859900 } /* Generic.Inserted */
-  .go { color: #93a1a1 } /* Generic.Output */
-  .gp { color: #93a1a1 } /* Generic.Prompt */
-  .gs { color: #93a1a1; font-weight: bold } /* Generic.Strong */
-  .gu { color: #cb4b16 } /* Generic.Subheading */
-  .gt { color: #93a1a1 } /* Generic.Traceback */
-  .kc { color: #cb4b16 } /* Keyword.Constant */
-  .kd { color: #268bd2 } /* Keyword.Declaration */
-  .kn { color: #859900 } /* Keyword.Namespace */
-  .kp { color: #859900 } /* Keyword.Pseudo */
-  .kr { color: #268bd2 } /* Keyword.Reserved */
-  .kt { color: #dc322f } /* Keyword.Type */
-  .ld { color: #93a1a1 } /* Literal.Date */
-  .m { color: #2aa198 } /* Literal.Number */
-  .s { color: #2aa198 } /* Literal.String */
-  .na { color: #93a1a1 } /* Name.Attribute */
-  .nb { color: #b58900 } /* Name.Builtin */
-  .nc { color: #268bd2 } /* Name.Class */
-  .no { color: #cb4b16 } /* Name.Constant */
-  .nd { color: #268bd2 } /* Name.Decorator */
-  .ni { color: #cb4b16 } /* Name.Entity */
-  .ne { color: #cb4b16 } /* Name.Exception */
-  .nf { color: #268bd2 } /* Name.Function */
-  .nl { color: #93a1a1 } /* Name.Label */
-  .nn { color: #93a1a1 } /* Name.Namespace */
-  .nx { color: #93a1a1 } /* Name.Other */
-  .py { color: #93a1a1 } /* Name.Property */
-  .nt { color: #268bd2 } /* Name.Tag */
-  .nv { color: #268bd2 } /* Name.Variable */
-  .ow { color: #859900 } /* Operator.Word */
-  .w { color: #93a1a1 } /* Text.Whitespace */
-  .mf { color: #2aa198 } /* Literal.Number.Float */
-  .mh { color: #2aa198 } /* Literal.Number.Hex */
-  .mi { color: #2aa198 } /* Literal.Number.Integer */
-  .mo { color: #2aa198 } /* Literal.Number.Oct */
-  .sb { color: #586e75 } /* Literal.String.Backtick */
-  .sc { color: #2aa198 } /* Literal.String.Char */
-  .sd { color: #93a1a1 } /* Literal.String.Doc */
-  .s2 { color: #2aa198 } /* Literal.String.Double */
-  .se { color: #cb4b16 } /* Literal.String.Escape */
-  .sh { color: #93a1a1 } /* Literal.String.Heredoc */
-  .si { color: #2aa198 } /* Literal.String.Interpol */
-  .sx { color: #2aa198 } /* Literal.String.Other */
-  .sr { color: #dc322f } /* Literal.String.Regex */
-  .s1 { color: #2aa198 } /* Literal.String.Single */
-  .ss { color: #2aa198 } /* Literal.String.Symbol */
-  .bp { color: #268bd2 } /* Name.Builtin.Pseudo */
-  .vc { color: #268bd2 } /* Name.Variable.Class */
-  .vg { color: #268bd2 } /* Name.Variable.Global */
-  .vi { color: #268bd2 } /* Name.Variable.Instance */
-  .il { color: #2aa198 } /* Literal.Number.Integer.Long */
+  .c { color: #586e75; } /* Comment */
+  .err { color: #93a1a1; } /* Error */
+  .g { color: #93a1a1; } /* Generic */
+  .k { color: #859900; } /* Keyword */
+  .l { color: #93a1a1; } /* Literal */
+  .n { color: #93a1a1; } /* Name */
+  .o { color: #859900; } /* Operator */
+  .x { color: #cb4b16; } /* Other */
+  .p { color: #93a1a1; } /* Punctuation */
+  .cm { color: #586e75; } /* Comment.Multiline */
+  .cp { color: #859900; } /* Comment.Preproc */
+  .c1 { color: #586e75; } /* Comment.Single */
+  .cs { color: #859900; } /* Comment.Special */
+  .gd { color: #2aa198; } /* Generic.Deleted */
+  .ge { color: #93a1a1; font-style: italic; } /* Generic.Emph */
+  .gr { color: #dc322f; } /* Generic.Error */
+  .gh { color: #cb4b16; } /* Generic.Heading */
+  .gi { color: #859900; } /* Generic.Inserted */
+  .go { color: #93a1a1; } /* Generic.Output */
+  .gp { color: #93a1a1; } /* Generic.Prompt */
+  .gs { color: #93a1a1; font-weight: bold; } /* Generic.Strong */
+  .gu { color: #cb4b16; } /* Generic.Subheading */
+  .gt { color: #93a1a1; } /* Generic.Traceback */
+  .kc { color: #cb4b16; } /* Keyword.Constant */
+  .kd { color: #268bd2; } /* Keyword.Declaration */
+  .kn { color: #859900; } /* Keyword.Namespace */
+  .kp { color: #859900; } /* Keyword.Pseudo */
+  .kr { color: #268bd2; } /* Keyword.Reserved */
+  .kt { color: #dc322f; } /* Keyword.Type */
+  .ld { color: #93a1a1; } /* Literal.Date */
+  .m { color: #2aa198; } /* Literal.Number */
+  .s { color: #2aa198; } /* Literal.String */
+  .na { color: #93a1a1; } /* Name.Attribute */
+  .nb { color: #b58900; } /* Name.Builtin */
+  .nc { color: #268bd2; } /* Name.Class */
+  .no { color: #cb4b16; } /* Name.Constant */
+  .nd { color: #268bd2; } /* Name.Decorator */
+  .ni { color: #cb4b16; } /* Name.Entity */
+  .ne { color: #cb4b16; } /* Name.Exception */
+  .nf { color: #268bd2; } /* Name.Function */
+  .nl { color: #93a1a1; } /* Name.Label */
+  .nn { color: #93a1a1; } /* Name.Namespace */
+  .nx { color: #93a1a1; } /* Name.Other */
+  .py { color: #93a1a1; } /* Name.Property */
+  .nt { color: #268bd2; } /* Name.Tag */
+  .nv { color: #268bd2; } /* Name.Variable */
+  .ow { color: #859900; } /* Operator.Word */
+  .w { color: #93a1a1; } /* Text.Whitespace */
+  .mf { color: #2aa198; } /* Literal.Number.Float */
+  .mh { color: #2aa198; } /* Literal.Number.Hex */
+  .mi { color: #2aa198; } /* Literal.Number.Integer */
+  .mo { color: #2aa198; } /* Literal.Number.Oct */
+  .sb { color: #586e75; } /* Literal.String.Backtick */
+  .sc { color: #2aa198; } /* Literal.String.Char */
+  .sd { color: #93a1a1; } /* Literal.String.Doc */
+  .s2 { color: #2aa198; } /* Literal.String.Double */
+  .se { color: #cb4b16; } /* Literal.String.Escape */
+  .sh { color: #93a1a1; } /* Literal.String.Heredoc */
+  .si { color: #2aa198; } /* Literal.String.Interpol */
+  .sx { color: #2aa198; } /* Literal.String.Other */
+  .sr { color: #dc322f; } /* Literal.String.Regex */
+  .s1 { color: #2aa198; } /* Literal.String.Single */
+  .ss { color: #2aa198; } /* Literal.String.Symbol */
+  .bp { color: #268bd2; } /* Name.Builtin.Pseudo */
+  .vc { color: #268bd2; } /* Name.Variable.Class */
+  .vg { color: #268bd2; } /* Name.Variable.Global */
+  .vi { color: #268bd2; } /* Name.Variable.Instance */
+  .il { color: #2aa198; } /* Literal.Number.Integer.Long */
 }
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index 657bb5e3cd964109a49e58ccdb12b59ef355bf06..5956a28cafe1d8651566b00ec9f7d80b40fb89d2 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -78,72 +78,72 @@
   green     #859900  operators, other keywords
   */
 
-  .c { color: #93a1a1 } /* Comment */
-  .err { color: #586e75 } /* Error */
-  .g { color: #586e75 } /* Generic */
-  .k { color: #859900 } /* Keyword */
-  .l { color: #586e75 } /* Literal */
-  .n { color: #586e75 } /* Name */
-  .o { color: #859900 } /* Operator */
-  .x { color: #cb4b16 } /* Other */
-  .p { color: #586e75 } /* Punctuation */
-  .cm { color: #93a1a1 } /* Comment.Multiline */
-  .cp { color: #859900 } /* Comment.Preproc */
-  .c1 { color: #93a1a1 } /* Comment.Single */
-  .cs { color: #859900 } /* Comment.Special */
-  .gd { color: #2aa198 } /* Generic.Deleted */
-  .ge { color: #586e75; font-style: italic } /* Generic.Emph */
-  .gr { color: #dc322f } /* Generic.Error */
-  .gh { color: #cb4b16 } /* Generic.Heading */
-  .gi { color: #859900 } /* Generic.Inserted */
-  .go { color: #586e75 } /* Generic.Output */
-  .gp { color: #586e75 } /* Generic.Prompt */
-  .gs { color: #586e75; font-weight: bold } /* Generic.Strong */
-  .gu { color: #cb4b16 } /* Generic.Subheading */
-  .gt { color: #586e75 } /* Generic.Traceback */
-  .kc { color: #cb4b16 } /* Keyword.Constant */
-  .kd { color: #268bd2 } /* Keyword.Declaration */
-  .kn { color: #859900 } /* Keyword.Namespace */
-  .kp { color: #859900 } /* Keyword.Pseudo */
-  .kr { color: #268bd2 } /* Keyword.Reserved */
-  .kt { color: #dc322f } /* Keyword.Type */
-  .ld { color: #586e75 } /* Literal.Date */
-  .m { color: #2aa198 } /* Literal.Number */
-  .s { color: #2aa198 } /* Literal.String */
-  .na { color: #586e75 } /* Name.Attribute */
-  .nb { color: #b58900 } /* Name.Builtin */
-  .nc { color: #268bd2 } /* Name.Class */
-  .no { color: #cb4b16 } /* Name.Constant */
-  .nd { color: #268bd2 } /* Name.Decorator */
-  .ni { color: #cb4b16 } /* Name.Entity */
-  .ne { color: #cb4b16 } /* Name.Exception */
-  .nf { color: #268bd2 } /* Name.Function */
-  .nl { color: #586e75 } /* Name.Label */
-  .nn { color: #586e75 } /* Name.Namespace */
-  .nx { color: #586e75 } /* Name.Other */
-  .py { color: #586e75 } /* Name.Property */
-  .nt { color: #268bd2 } /* Name.Tag */
-  .nv { color: #268bd2 } /* Name.Variable */
-  .ow { color: #859900 } /* Operator.Word */
-  .w { color: #586e75 } /* Text.Whitespace */
-  .mf { color: #2aa198 } /* Literal.Number.Float */
-  .mh { color: #2aa198 } /* Literal.Number.Hex */
-  .mi { color: #2aa198 } /* Literal.Number.Integer */
-  .mo { color: #2aa198 } /* Literal.Number.Oct */
-  .sb { color: #93a1a1 } /* Literal.String.Backtick */
-  .sc { color: #2aa198 } /* Literal.String.Char */
-  .sd { color: #586e75 } /* Literal.String.Doc */
-  .s2 { color: #2aa198 } /* Literal.String.Double */
-  .se { color: #cb4b16 } /* Literal.String.Escape */
-  .sh { color: #586e75 } /* Literal.String.Heredoc */
-  .si { color: #2aa198 } /* Literal.String.Interpol */
-  .sx { color: #2aa198 } /* Literal.String.Other */
-  .sr { color: #dc322f } /* Literal.String.Regex */
-  .s1 { color: #2aa198 } /* Literal.String.Single */
-  .ss { color: #2aa198 } /* Literal.String.Symbol */
-  .bp { color: #268bd2 } /* Name.Builtin.Pseudo */
-  .vc { color: #268bd2 } /* Name.Variable.Class */
-  .vg { color: #268bd2 } /* Name.Variable.Global */
-  .vi { color: #268bd2 } /* Name.Variable.Instance */
-  .il { color: #2aa198 } /* Literal.Number.Integer.Long */
+  .c { color: #93a1a1; } /* Comment */
+  .err { color: #586e75; } /* Error */
+  .g { color: #586e75; } /* Generic */
+  .k { color: #859900; } /* Keyword */
+  .l { color: #586e75; } /* Literal */
+  .n { color: #586e75; } /* Name */
+  .o { color: #859900; } /* Operator */
+  .x { color: #cb4b16; } /* Other */
+  .p { color: #586e75; } /* Punctuation */
+  .cm { color: #93a1a1; } /* Comment.Multiline */
+  .cp { color: #859900; } /* Comment.Preproc */
+  .c1 { color: #93a1a1; } /* Comment.Single */
+  .cs { color: #859900; } /* Comment.Special */
+  .gd { color: #2aa198; } /* Generic.Deleted */
+  .ge { color: #586e75; font-style: italic; } /* Generic.Emph */
+  .gr { color: #dc322f; } /* Generic.Error */
+  .gh { color: #cb4b16; } /* Generic.Heading */
+  .gi { color: #859900; } /* Generic.Inserted */
+  .go { color: #586e75; } /* Generic.Output */
+  .gp { color: #586e75; } /* Generic.Prompt */
+  .gs { color: #586e75; font-weight: bold; } /* Generic.Strong */
+  .gu { color: #cb4b16; } /* Generic.Subheading */
+  .gt { color: #586e75; } /* Generic.Traceback */
+  .kc { color: #cb4b16; } /* Keyword.Constant */
+  .kd { color: #268bd2; } /* Keyword.Declaration */
+  .kn { color: #859900; } /* Keyword.Namespace */
+  .kp { color: #859900; } /* Keyword.Pseudo */
+  .kr { color: #268bd2; } /* Keyword.Reserved */
+  .kt { color: #dc322f; } /* Keyword.Type */
+  .ld { color: #586e75; } /* Literal.Date */
+  .m { color: #2aa198; } /* Literal.Number */
+  .s { color: #2aa198; } /* Literal.String */
+  .na { color: #586e75; } /* Name.Attribute */
+  .nb { color: #b58900; } /* Name.Builtin */
+  .nc { color: #268bd2; } /* Name.Class */
+  .no { color: #cb4b16; } /* Name.Constant */
+  .nd { color: #268bd2; } /* Name.Decorator */
+  .ni { color: #cb4b16; } /* Name.Entity */
+  .ne { color: #cb4b16; } /* Name.Exception */
+  .nf { color: #268bd2; } /* Name.Function */
+  .nl { color: #586e75; } /* Name.Label */
+  .nn { color: #586e75; } /* Name.Namespace */
+  .nx { color: #586e75; } /* Name.Other */
+  .py { color: #586e75; } /* Name.Property */
+  .nt { color: #268bd2; } /* Name.Tag */
+  .nv { color: #268bd2; } /* Name.Variable */
+  .ow { color: #859900; } /* Operator.Word */
+  .w { color: #586e75; } /* Text.Whitespace */
+  .mf { color: #2aa198; } /* Literal.Number.Float */
+  .mh { color: #2aa198; } /* Literal.Number.Hex */
+  .mi { color: #2aa198; } /* Literal.Number.Integer */
+  .mo { color: #2aa198; } /* Literal.Number.Oct */
+  .sb { color: #93a1a1; } /* Literal.String.Backtick */
+  .sc { color: #2aa198; } /* Literal.String.Char */
+  .sd { color: #586e75; } /* Literal.String.Doc */
+  .s2 { color: #2aa198; } /* Literal.String.Double */
+  .se { color: #cb4b16; } /* Literal.String.Escape */
+  .sh { color: #586e75; } /* Literal.String.Heredoc */
+  .si { color: #2aa198; } /* Literal.String.Interpol */
+  .sx { color: #2aa198; } /* Literal.String.Other */
+  .sr { color: #dc322f; } /* Literal.String.Regex */
+  .s1 { color: #2aa198; } /* Literal.String.Single */
+  .ss { color: #2aa198; } /* Literal.String.Symbol */
+  .bp { color: #268bd2; } /* Name.Builtin.Pseudo */
+  .vc { color: #268bd2; } /* Name.Variable.Class */
+  .vg { color: #268bd2; } /* Name.Variable.Global */
+  .vi { color: #268bd2; } /* Name.Variable.Instance */
+  .il { color: #2aa198; } /* Literal.Number.Integer.Long */
 }
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index 36a80a916b2df218d3a9420b20528712fa4db5ce..6f31a5235c0d5a011c13702130937eeba132abde 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -86,7 +86,7 @@
     background-color: #fafe3d !important;
   }
 
-  .hll { background-color: #f8f8f8 }
+  .hll { background-color: #f8f8f8; }
   .c { color: #998; font-style: italic; }
   .err { color: #a61717; background-color: #e3d2d2; }
   .k { font-weight: bold; }
diff --git a/app/assets/stylesheets/mailers/repository_push_email.scss b/app/assets/stylesheets/mailers/repository_push_email.scss
index 5bfe9bcb443dd9a9aff5d13dd505f5751165fa04..8d1a6020ca4a4eafd32f4dd9eee81713119faa51 100644
--- a/app/assets/stylesheets/mailers/repository_push_email.scss
+++ b/app/assets/stylesheets/mailers/repository_push_email.scss
@@ -78,7 +78,7 @@ span.highlight_word {
   background-color: #fafe3d !important;
 }
 
-.hll { background-color: #f8f8f8 }
+.hll { background-color: #f8f8f8; }
 .c { color: #998; font-style: italic; }
 .err { color: #a61717; background-color: #e3d2d2; }
 .k { font-weight: bold; }
diff --git a/app/assets/stylesheets/notify.scss b/app/assets/stylesheets/notify.scss
index fc12964872d2ebfca0f210dc37a6817f0a4e89cc..ced8c4a99075160aa26a87b58a46d4002fee4e76 100644
--- a/app/assets/stylesheets/notify.scss
+++ b/app/assets/stylesheets/notify.scss
@@ -2,22 +2,28 @@ img {
   max-width: 100%;
   height: auto;
 }
+
 p.details {
   font-style: italic;
-  color: #777
+  color: #777;
 }
+
 .footer > p {
   font-size: small;
-  color: #777
+  color: #777;
 }
+
 pre.commit-message {
   white-space: pre-wrap;
 }
+
 .file-stats > a {
   text-decoration: none;
+
   > .new-file {
     color: #090;
   }
+
   > .deleted-file {
     color: #b00;
   }
diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss
index 8f71381f5c4e671039d05d774459e6f3224af9f0..140d589024bf2c471a660c3d489d8311f739b769 100644
--- a/app/assets/stylesheets/pages/admin.scss
+++ b/app/assets/stylesheets/pages/admin.scss
@@ -22,7 +22,7 @@
 
 .admin-filter form {
   .select2-container {
-    width: 100%
+    width: 100%;
   }
 
   .controls {
@@ -31,7 +31,7 @@
 
   .form-actions {
     padding-left: 130px;
-    background: #fff
+    background: #fff;
    }
 
   .visibility-levels {
@@ -106,26 +106,33 @@
   .table {
     table-layout: fixed;
   }
+
   .subheading {
     padding-bottom: $gl-padding;
   }
+
   .message {
     word-wrap: break-word;
   }
+
   .btn {
     white-space: normal;
     padding: $gl-btn-padding;
   }
+
   th {
     width: 15%;
+
     &.wide {
       width: 55%;
     }
   }
+
   @media (max-width: $screen-sm-max) {
     th {
       width: 100%;
     }
+
     td {
       width: 100%;
       float: left;
@@ -137,6 +144,7 @@
       margin-left: $btn-side-margin;
       margin-top: 3px;
     }
+
     span {
       font-size: 19px;
     }
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 194a39a8377d3fe4f68886855cf73054911f5421..2fbf0cf34bfd7df2a9a7cec44516a8033b83e63b 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -137,6 +137,7 @@
 
   .retry-link {
     color: $gl-link-color;
+
     &:hover {
       text-decoration: underline;
     }
@@ -218,6 +219,7 @@
 
 .build-detail-row {
   margin-bottom: 5px;
+
   &:last-of-type {
     margin-bottom: 0;
   }
@@ -233,3 +235,9 @@
   right: 0;
   margin-top: -17px;
 }
+
+@media (min-width: $screen-md-min) {
+  .sub-nav.build {
+    width: calc(100% + #{$gutter_width});
+  }
+}
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index 53ec0002afed6a60016c499b556e2854bb3c21a9..264e7e01a347518b35dea81cc4d640e5ab20bc5a 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -51,6 +51,7 @@
       margin-left: 4px;
     }
   }
+
   .commit-committer-link,
   .commit-author-link {
     color: $gl-gray;
@@ -108,21 +109,25 @@
       line-height: 20px;
     }
   }
+
   .new-file {
     a {
       color: $gl-text-green;
     }
   }
+
   .renamed-file {
     a {
       color: $gl-text-orange;
     }
   }
+
   .deleted-file {
     a {
       color: $gl-text-red;
     }
   }
+
   .edit-file {
     a {
       color: $gl-text-color;
@@ -158,6 +163,7 @@
     position: absolute;
     z-index: 1;
   }
+
   > textarea {
     background-color: rgba(0, 0, 0, 0.0);
     font-family: inherit;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index dc57a8371558c398e7f2f8090993a5952c2b13e7..2b5621e20d668ee11ef334598b0ae14229f2d8a1 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -161,6 +161,7 @@
 
 .branch-commit {
   color: $gl-gray;
+
   .commit-id, .commit-row-message {
     color: $gl-gray;
   }
diff --git a/app/assets/stylesheets/pages/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss
index 42928ee279c252c15401dc91f83eb08344ec0ceb..76225ed8d066c56da3aef4b97f8fe2e6762844b7 100644
--- a/app/assets/stylesheets/pages/dashboard.scss
+++ b/app/assets/stylesheets/pages/dashboard.scss
@@ -5,6 +5,7 @@
         background: $background-color;
         border-top-left-radius: 0;
       }
+
       border-top-left-radius: 0;
     }
   }
@@ -17,6 +18,7 @@
     float: left;
     @extend .col-md-2;
   }
+
   .btn {
     margin-left: 5px;
     float: left;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index b8ef76cc74e2d7b1343585b84f4d68b3da7fbe6e..bdc82a8f0f5dd47febf0d6154972b0ee7e1278cd 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -33,6 +33,19 @@
       font-size: smaller;
     }
   }
+
+  .file-title {
+    cursor: pointer;
+
+    &:hover {
+      background-color: $dark-background-color;
+    }
+
+    .diff-toggle-caret {
+      padding-right: 6px;
+    }
+  }
+
   .diff-content {
     overflow: auto;
     overflow-y: hidden;
@@ -123,15 +136,18 @@
       max-width: 50px;
       width: 35px;
       @include user-select(none);
+
       a {
         float: left;
         width: 35px;
         font-weight: normal;
+
         &:hover {
           text-decoration: underline;
         }
       }
     }
+
     .line_content {
       display: block;
       margin: 0;
@@ -151,10 +167,12 @@
       white-space: pre-wrap;
     }
   }
+
   .image {
     background: #ddd;
     text-align: center;
     padding: 30px;
+
     .wrap {
       display: inline-block;
     }
@@ -163,6 +181,7 @@
       display: inline-block;
       background-color: #fff;
       line-height: 0;
+
       img {
         border: 1px solid #fff;
         background-image: linear-gradient(45deg, #e5e5e5 25%, transparent 25%, transparent 75%, #e5e5e5 75%, #e5e5e5 100%),
@@ -171,6 +190,7 @@
         background-position: 0 0, 5px 5px;
         max-width: 100%;
       }
+
       &.deleted {
         border: 1px solid $deleted;
       }
@@ -179,6 +199,7 @@
         border: 1px solid $added;
       }
     }
+
     .image-info {
       font-size: 12px;
       margin: 5px 0 0;
@@ -193,6 +214,7 @@
         margin: auto;
         position: relative;
       }
+
       .swipe-wrap {
         overflow: hidden;
         border-left: 1px solid #999;
@@ -201,10 +223,12 @@
         top: 13px;
         right: 7px;
       }
+ 
       .frame {
         top: 0;
         right: 0;
         position: absolute;
+ 
         &.deleted {
           margin: 0;
           display: block;
@@ -212,6 +236,7 @@
           right: 7px;
         }
       }
+
       .swipe-bar {
         display: block;
         height: 100%;
@@ -219,14 +244,17 @@
         z-index: 100;
         position: absolute;
         cursor: pointer;
+
         &:hover {
           .top-handle {
             background-position: -15px 3px;
           }
+
           .bottom-handle {
             background-position: -15px -11px;
           }
         }
+
         .top-handle {
           display: block;
           height: 14px;
@@ -235,6 +263,7 @@
           top: 0;
           background: image-url('swipemode_sprites.gif') 0 3px no-repeat;
         }
+
         .bottom-handle {
           display: block;
           height: 14px;
@@ -252,12 +281,14 @@
         margin: auto;
         position: relative;
       }
+
       .frame.added, .frame.deleted {
         position: absolute;
         display: block;
         top: 0;
         left: 0;
       }
+
       .controls {
         display: block;
         height: 14px;
@@ -311,6 +342,7 @@
     }
     //.view.onion-skin
   }
+
   .view-modes {
     padding: 10px;
     text-align: center;
@@ -328,19 +360,24 @@
       border-left: 1px solid #c1c1c1;
       padding: 0 12px 0 16px;
       cursor: pointer;
+
       &:first-child {
         border-left: none;
       }
+
       &:hover {
         text-decoration: underline;
       }
+
       &.active {
         &:hover {
           text-decoration: none;
         }
+
         cursor: default;
         color: #333;
       }
+
       &.disabled {
         display: none;
       }
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index fcc5f32c7380ac56f570eaefc23e5d8fb0bb8c50..029dabd21380bda6e5fc273e622d2f3a9c02054e 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -15,6 +15,7 @@
 
   .cancel-btn {
     color: #b94a48;
+
     &:hover {
       color: #b94a48;
     }
@@ -70,16 +71,20 @@
 
   .soft-wrap-toggle {
     margin: 0 $btn-side-margin;
+
     .soft-wrap {
       display: block;
     }
+
     .no-wrap {
       display: none;
     }
+
     &.soft-wrap-active {
       .soft-wrap {
         display: none;
       }
+
       .no-wrap {
         display: block;
       }
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 3f19e9201667d49961eef953c2d9f53ae405036a..820cc0fc991a54f8773e376b451419b153a6f2af 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -52,13 +52,13 @@
   }
 }
 
-.table.builds.environments {
+.table.ci-table.environments {
 
   .icon-container {
     width: 20px;
     text-align: center;
   }
-  
+
   .branch-commit {
     .commit-id {
       margin-right: 0;
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 789d6237df85c249428a27468f0c2c0fc5830366..5d9a76dac0535eaf7bebc1bc506c1e25ec62908f 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -78,6 +78,7 @@
         margin-bottom: 0;
       }
     }
+
     .event-note-icon {
       color: #777;
       float: left;
@@ -86,6 +87,7 @@
       margin-right: 5px;
     }
   }
+
   .event_icon {
     position: relative;
     float: right;
@@ -95,12 +97,13 @@
     background: $gray-light;
     margin-left: 10px;
     top: -6px;
+
     img {
       width: 20px;
     }
   }
 
-  &:last-child { border: none }
+  &:last-child { border: none; }
 
   .event_commits {
     li {
@@ -109,6 +112,7 @@
         padding: 3px;
         padding-left: 0;
         border: none;
+
         .commit-row-title {
           font-size: $gl-font-size;
         }
@@ -117,6 +121,7 @@
       &.commits-stat {
         display: block;
         padding: 0 3px 0 0;
+
         &:hover {
           background: none;
         }
@@ -158,6 +163,7 @@
       overflow: visible;
       max-width: 100%;
     }
+
     .avatar {
       display: none;
     }
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 185ce970e719fe72086185cd2c8ceeca52e7de76..ee2a398f031ff6498b1b797bbc26323f0036b134 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -1,17 +1,3 @@
-.member-search-form {
-  float: left;
-
-  input[type='search'] {
-    width: 225px;
-    vertical-align: bottom;
-
-    @media (max-width: $screen-xs-max) {
-      width: 100px;
-      vertical-align: bottom;
-    }
-  }
-}
-
 .milestone-row {
   @include str-truncated(90%);
 }
@@ -48,6 +34,7 @@
   .group-right-buttons {
     position: absolute;
     right: 16px;
+
     .btn {
       @include btn-gray;
       padding: 3px 10px;
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index 00ab42bec5cb75c36c0efcf35029c8d6f0bc5d1b..a48b4c65db8ea6af15b13e28a2f2ed1c224ca6b2 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -23,28 +23,28 @@
   color: #555;
 
   tbody:first-child tr:first-child {
-    padding-top: 0
+    padding-top: 0;
   }
 
   th {
     padding-top: 15px;
     line-height: 1.5;
     color: #333;
-    text-align: left
+    text-align: left;
   }
 
   td {
     padding-top: 3px;
     padding-bottom: 3px;
     vertical-align: top;
-    line-height: 20px
+    line-height: 20px;
   }
 
   .shortcut {
     padding-right: 10px;
     color: #999;
     text-align: right;
-    white-space: nowrap
+    white-space: nowrap;
   }
 
   .key {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 41079b6eeb55b988a9dba874b82d96320eff6f6d..230b927a17daaefd7d5d9786b8dbd40c108a5949 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -27,6 +27,7 @@
     margin-right: 5px;
     margin-bottom: 5px;
     display: inline-block;
+
     .color-label {
       padding: 6px 10px;
     }
@@ -128,7 +129,7 @@
   }
 
   .selectbox {
-    display: none
+    display: none;
   }
 
   .btn-clipboard {
@@ -199,7 +200,7 @@
     display: none;
     /* Small devices (tablets, 768px and up) */
     @media (min-width: $screen-sm-min) {
-      display: block
+      display: block;
     }
 
     width: $sidebar_collapsed_width;
@@ -276,7 +277,7 @@
     }
 
     &.btn-primary {
-      @extend .btn-primary
+      @extend .btn-primary;
     }
   }
 
@@ -400,6 +401,7 @@
   .js-issuable-selector {
     width: 100%;
   }
+
   @media (max-width: $screen-sm-max) {
     margin-bottom: $gl-padding;
   }
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 3ac34cbc829f1a3bf64435626973dd0fc9054ed9..623da67a239693e722ce603a56dc0e079e2076f1 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -37,6 +37,7 @@ ul.related-merge-requests > li {
   display: -ms-flexbox;
   display: -webkit-flex;
   display: flex;
+
   .merge-request-id {
     flex-shrink: 0;
   }
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 701c29a3986e439c32ff9e9b80bbfee3e18851e2..9bac6d463551c2cbb4190e9d4cdc073a11d2f574 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -1,5 +1,6 @@
 .suggest-colors {
   margin-top: 5px;
+
   a {
     border-radius: 4px;
     width: 30px;
diff --git a/app/assets/stylesheets/pages/lint.scss b/app/assets/stylesheets/pages/lint.scss
index 6926448519e32663effa1342d0e9ecf77b1a0017..8290519dc258074907a97e61638d4a38a07877f7 100644
--- a/app/assets/stylesheets/pages/lint.scss
+++ b/app/assets/stylesheets/pages/lint.scss
@@ -3,6 +3,7 @@
     font-size: 19px;
     color: red;
   }
+
   .correct-syntax {
     font-size: 19px;
     color: #47a447;
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index a5ca509163df39778a62bd9fdb4e86f13d0a6055..4c0c0839fe2c6abc592cfd8511b0d38cceb4d5cc 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -17,6 +17,7 @@
     line-height: 1.5;
 
     p {
+      font-size: 18px;
       color: #888;
     }
 
@@ -36,10 +37,14 @@
     }
   }
 
-  .login-box {
-    background: #fafafa;
-    border-radius: 10px;
-    box-shadow: 0 0 2px #ccc;
+  p {
+    font-size: 13px;
+  }
+
+  .login-box, .omniauth-container {
+    box-shadow: 0 0 0 1px $border-color;
+    border-bottom-right-radius: 2px;
+    border-bottom-left-radius: 2px;
     padding: 15px;
 
     .login-heading h3 {
@@ -58,42 +63,127 @@
 
     a.forgot {
       float: right;
-      padding-top: 6px
+      padding-top: 6px;
     }
 
     .nav .active a {
       background: transparent;
     }
-  }
 
-  .form-control {
-    font-size: 14px;
-    padding: 10px 8px;
-    width: 100%;
-    height: auto;
-
-    &.top {
-      border-radius: 5px 5px 0 0;
-      margin-bottom: 0;
+    // Styles the glowing border of focused input for username async validation
+    .login-body {
+      font-size: 13px;
+
+
+      input + p {
+        margin-top: 5px;
+      }
+
+      .gl-field-success-outline {
+        border: 1px solid $green-normal;
+
+        &:focus {
+          box-shadow: 0 0 0 1px $green-normal inset, 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 4px 0 $green-normal;
+          border: 0 none;
+        }
+      }
+
+      .gl-field-error-outline {
+        border: 1px solid $red-normal;
+
+        &:focus {
+          box-shadow: 0 0 0 1px $red-normal inset, 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 4px 0 rgba(210, 40, 82, 0.6);
+          border: 0 none;
+        }
+      }
+
+      .username .validation-success,
+      .gl-field-success-message {
+        color: $green-normal;
+      }
+
+      .username .validation-error,
+      .gl-field-error-message {
+        color: $red-normal;
+      }
+
+      .gl-field-hint {
+        color: $gl-text-color;
+      }
+
     }
+  }
 
-    &.bottom {
-      border-radius: 0 0 5px 5px;
-      border-top: 0;
-      margin-bottom: 20px;
+  .omniauth-container {
+    p {
+      margin: 0;
     }
+  }
+
+  .new-session-tabs {
+    display: -webkit-flex;
+    display: flex;
+    box-shadow: 0 0 0 1px $border-color;
+    border-top-right-radius: 2px;
+    border-top-left-radius: 2px;
+
+    li {
+      flex: 1;
+      text-align: center;
+
+      &:last-of-type {
+        border-left: 1px solid $border-color;
+      }
+
+      &:not(.active) {
+        background-color: $gray-light;
+      }
+
+      a {
+        width: 100%;
+        font-size: 18px;
 
-    &.middle {
-      border-top: 0;
-      margin-bottom: 0;
-      border-radius: 0;
+        &:hover {
+          border: 1px solid transparent;
+        }
+      }
+
+      &.active {
+        border-bottom: 1px solid $border-color;
+
+        a {
+          border: none;
+          border-bottom: 2px solid $link-underline-blue;
+          color: $black;
+
+          &:hover {
+            border-bottom: 2px solid $link-underline-blue;
+          }
+        }
+      }
     }
+  }
 
+
+  .form-control {
     &:active, &:focus {
       background-color: #fff;
     }
   }
 
+  label {
+    font-weight: normal;
+  }
+
+  .submit-container {
+    margin-top: 16px;
+  }
+
+  input[type="submit"] {
+    @extend .btn-block;
+    margin-bottom: 0;
+  }
+
   .devise-errors {
     h2 {
       margin-top: 0;
@@ -101,14 +191,6 @@
       color: #a00;
     }
   }
-
-  .remember-me {
-    margin-top: -10px;
-
-    label {
-      font-weight: normal;
-    }
-  }
 }
 
 @media (max-width: $screen-xs-max) {
@@ -127,3 +209,35 @@
     height: 32px;
   }
 }
+
+.devise-layout-html {
+  margin: 0;
+  padding: 0;
+  height: 100%;
+}
+
+// Fixes footer container to bottom of viewport
+.devise-layout-html body {
+  // offset height of fixed header + 1 to avoid scroll
+  height: calc(100% - 51px);
+  margin: 0;
+  padding: 0;
+
+  .page-wrap {
+    min-height: 100%;
+    position: relative;
+  }
+
+  .footer-container, hr.footer-fixed {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    height: 40px;
+    background: $white-light;
+  }
+
+  .navless-container {
+    padding: 65px; // height of footer + bottom padding of email confirmation link
+  }
+}
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
new file mode 100644
index 0000000000000000000000000000000000000000..756efa9c7fa027433b0643a602b32f02e10c0166
--- /dev/null
+++ b/app/assets/stylesheets/pages/members.scss
@@ -0,0 +1,98 @@
+.project-members-title {
+  padding-bottom: 10px;
+  border-bottom: 1px solid $border-color;
+}
+
+.member {
+  .list-item-name {
+    @media (min-width: $screen-sm-min) {
+      float: left;
+      width: 50%;
+    }
+
+    strong {
+      font-weight: 600;
+    }
+  }
+
+  .controls {
+    @media (min-width: $screen-sm-min) {
+      display: -webkit-flex;
+      display: flex;
+      width: 400px;
+      max-width: 50%;
+    }
+  }
+
+  .form-horizontal {
+    margin-top: 5px;
+
+    @media (min-width: $screen-sm-min) {
+      display: -webkit-flex;
+      display: flex;
+      width: 100%;
+      margin-top: 3px;
+    }
+  }
+
+  .btn-remove {
+    width: 100%;
+
+    @media (min-width: $screen-sm-min) {
+      width: auto;
+    }
+  }
+}
+
+.member-form-control {
+  @media (max-width: $screen-xs-max) {
+    padding: 5px 0;
+    margin-left: 0;
+    margin-right: 0;
+  }
+
+  @media (min-width: $screen-sm-min) {
+    width: 50%;
+  }
+}
+
+.member-access-text {
+  margin-left: auto;
+  line-height: 43px;
+}
+
+.member.existing-title {
+  @media (min-width: $screen-sm-min) {
+    float: left;
+  }
+}
+
+.member-search-form {
+  position: relative;
+
+  @media (min-width: $screen-sm-min) {
+    float: right;
+  }
+
+  .form-control {
+    width: 100%;
+    padding-right: 35px;
+
+    @media (min-width: $screen-sm-min) {
+      width: 350px;
+    }
+  }
+}
+
+.member-search-btn {
+  position: absolute;
+  right: 0;
+  top: 0;
+  height: 35px;
+  padding-left: 10px;
+  padding-right: 10px;
+  color: $gray-darkest;
+  background: transparent;
+  border: 0;
+  outline: 0;
+}
diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss
index 5ec660799e366f8a78c07124a52f39a220b7be7a..49013d7cac9ada5416537fe26b6e8ebcbc756393 100644
--- a/app/assets/stylesheets/pages/merge_conflicts.scss
+++ b/app/assets/stylesheets/pages/merge_conflicts.scss
@@ -131,6 +131,7 @@ $colors: (
         }
       }
     }
+
     &.head {
       background-color: map-get($colors, #{$color}_header_head_neutral);
       border-color: map-get($colors, #{$color}_header_head_neutral);
@@ -174,6 +175,7 @@ $colors: (
         background-color: map-get($colors, #{$color}_line_not_chosen);
       }
     }
+
     &.head {
       background-color: map-get($colors, #{$color}_line_head_neutral);
 
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 7cf69c56d15f1977a2c1896e0e25983210274015..afc4e517fde3f556cfcb8c03118b8131e644e44d 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -10,6 +10,7 @@
 
   form {
     margin-bottom: 0;
+
     .clearfix {
       margin-bottom: 0;
     }
@@ -46,6 +47,7 @@
 
       &.right {
         float: right;
+
         a {
           color: $gl-gray;
         }
@@ -121,6 +123,10 @@
     color: #5c5d5e;
   }
 
+  .js-deployment-link {
+    display: inline-block;
+  }
+
   .mr-widget-body {
     h4 {
       font-weight: 600;
@@ -188,6 +194,7 @@
     padding-top: 2px;
     padding-bottom: 2px;
     list-style: none;
+
     &:hover {
       background: none;
     }
@@ -211,6 +218,7 @@
     padding-top: 20px;
     padding-bottom: 10px;
   }
+
   svg {
     width: 230px;
   }
@@ -276,12 +284,6 @@
   line-height: 31px;
 }
 
-.builds {
-  .table-holder {
-    overflow-x: auto;
-  }
-}
-
 .panel-new-merge-request {
   .panel-heading {
     padding: 5px 10px;
@@ -369,7 +371,7 @@
 }
 
 .table-holder {
-  .builds {
+  .ci-table {
 
     th {
       background-color: $white-light;
@@ -433,3 +435,12 @@
     margin-bottom: 20px;
   }
 }
+
+.merge-request-tabs {
+  background-color: #fff;
+
+  &.affix {
+    top: 100px;
+    z-index: 9;
+  }
+}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 8c2ba3ed58cbd9c4678dbef95f9bf819d8fc3443..dd6d17836678724237155d8f686bb57070013590 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -59,6 +59,7 @@
       color: $gl-placeholder-color;
       margin-right: 5px;
     }
+
     .avatar {
       float: none;
     }
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index bd875b9823ffed1efebbc513110648bc5289f34f..17f28959414c5db1077d246907f4578f9fdfe83b 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -11,6 +11,7 @@
     filter: alpha(opacity=100);
   }
 }
+
 .diff-file,
 .discussion {
   .new-note {
@@ -194,6 +195,7 @@
     min-height: 140px;
     max-height: 500px;
   }
+
   .note-form-actions {
     background: transparent;
   }
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index d399f84a2ffc16191181665ff224bc741e1dd881..fffcdc812a72f1a865d48c1a891105e31f20ff23 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -147,9 +147,18 @@ ul.notes {
 
 // Diff code in discussion view
 .discussion-body .diff-file {
+  .file-title {
+    cursor: default;
+
+    &:hover {
+      background-color: $gray-light;
+    }
+  }
+
   .diff-header > span {
     margin-right: 10px;
   }
+
   .line_content {
     white-space: pre-wrap;
   }
@@ -345,6 +354,7 @@ ul.notes {
     width: 32px;
     // "hide" it by default
     display: none;
+
     &:hover {
       background: $gl-info;
       color: #fff;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 7843355f0ab1b2051df5e0462a80f910e7da0656..7b71876b8226ec300ccfba954da4c7c976ea3291 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -20,7 +20,7 @@
     margin: 4px;
   }
 
-  .table.builds {
+  .table.ci-table {
     min-width: 1200px;
 
     .branch-commit {
@@ -44,13 +44,20 @@
   overflow: auto;
 }
 
-.table.builds {
+.table.ci-table {
   min-width: 900px;
 
   &.pipeline {
     min-width: 650px;
   }
 
+  &.builds-page {
+
+    tr {
+      height: 71px;
+    }
+  }
+
   tr {
     th {
       padding: 16px 8px;
@@ -176,7 +183,7 @@
         &::after {
           content: '';
           width: 8px;
-          position: absolute;;
+          position: absolute;
           right: -7px;
           bottom: 8px;
           border-bottom: 2px solid $border-color;
@@ -353,6 +360,7 @@
 
     &:hover {
       background-color: $gray-lighter;
+
       .dropdown-menu-toggle {
         background-color: transparent;
       }
@@ -536,6 +544,7 @@
         height: 29px;
         top: -9px;
       }
+
       .curve {
         display: block;
       }
@@ -621,19 +630,31 @@
   }
 }
 
-.pipelines.tab-pane {
+.tab-pane {
 
-  .content-list.pipelines {
-    overflow: auto;
-  }
+  &.pipelines {
 
-  .stage {
-    max-width: 100px;
-    width: 100px;
+    .content-list.pipelines {
+      overflow: auto;
+    }
+
+    .stage {
+      max-width: 100px;
+      width: 100px;
+    }
+
+    .pipeline-actions {
+      min-width: initial;
+    }
   }
 
-  .pipeline-actions {
-    min-width: initial;
+  &.builds {
+
+    .ci-table {
+      tr {
+        height: 71px;
+      }
+    }
   }
 }
 
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index c7eac5cf4b9a10384b622bde006c1959472916c5..ed80d2beec26443adb2727d7c1fb219a5cc14afd 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -243,6 +243,7 @@
       .btn {
         -webkit-flex-grow: 1;
         flex-grow: 1;
+
         &:first-child {
           margin-left: 0;
         }
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 530fb0c0d056aefb37a2963fafe12a4fb659e93f..d30f02340b9b945f5bca413a5e8cd31a8a879860 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -17,34 +17,43 @@
     &.features .control-label {
       font-weight: normal;
     }
+
     .form-group {
       margin-bottom: 5px;
     }
+
     &> .form-group {
       padding-left: 0;
     }
   }
+
   .help-block {
     margin-bottom: 10px;
   }
+
   .project-path {
     padding-right: 0;
+
     .form-control {
       border-radius: $border-radius-base;
     }
   }
+
   .input-group > div {
     &:last-child {
       padding-right: 0;
     }
   }
+
   @media (max-width: $screen-xs-max) {
     .input-group > div {
       margin-bottom: 14px;
+
       &:last-child {
         margin-bottom: 0;
       }
     }
+
     fieldset > .form-group:first-child {
       padding-right: 0;
     }
@@ -56,6 +65,7 @@
       border-radius: 3px;
       border: 1px solid #e5e5e5;
     }
+
     &+ .select2 a {
       border-top-left-radius: 0;
       border-bottom-left-radius: 0;
@@ -201,6 +211,7 @@
         pointer-events: none;
       }
     }
+
     .count {
       @include btn-gray;
       display: inline-block;
@@ -361,10 +372,12 @@ a.deploy-project-label {
       margin: $gl-padding;
       text-align: center;
       width: 169px;
+
       &:hover, &.forked {
         background-color: $row-hover;
         border-color: $row-hover-border;
       }
+
       .no-avatar {
         width: 100px;
         height: 100px;
@@ -372,17 +385,20 @@ a.deploy-project-label {
         border: 1px solid $gray-dark;
         margin: 0 auto;
         border-radius: 50%;
+
         i {
           font-size: 100px;
           color: $gray-dark;
         }
       }
+
       a {
         display: block;
         width: 100%;
         height: 100%;
         padding-top: $gl-padding;
         color: $gl-gray;
+
         .caption {
           min-height: 30px;
           padding: $gl-padding 0;
@@ -644,6 +660,7 @@ pre.light-well {
 
   .clone-options {
     display: table-cell;
+
     a.btn {
       width: 100%;
     }
@@ -832,6 +849,7 @@ pre.light-well {
   .form-control {
     min-width: 100px;
   }
+
   .select2-choice {
     border-top-right-radius: 0;
     border-bottom-right-radius: 0;
diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss
index eec22c5dc960384eb7caae00d6263a462afd4e51..7b3878c91df64a41764d44592ccd3ba25388c384 100644
--- a/app/assets/stylesheets/pages/runners.scss
+++ b/app/assets/stylesheets/pages/runners.scss
@@ -6,6 +6,7 @@
   &.runner-state-shared {
     background: #32b186;
   }
+
   &.runner-state-specific {
     background: #3498db;
   }
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index c05f3d5ff32d35cceb0d408ab8ad6ea4bffaefca..f1d53c7b8bc689779ee30dc827c1af938971f0f4 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -65,6 +65,7 @@
   .ci-status-icon-success {
     color: $gl-success;
   }
+
   .ci-status-icon-failed {
     color: $gl-danger;
   }
@@ -77,6 +78,7 @@
   .ci-status-icon-running {
     color: $blue-normal;
   }
+
   .ci-status-icon-canceled,
   .ci-status-icon-disabled,
   .ci-status-icon-not-found,
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 41ad10f07bd3a660de59cb4a53e85e823fc683a1..99c0f6362d0bd1230a0216066ceadc6c53546f8b 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -5,6 +5,7 @@
 
   .file-finder {
     width: 50%;
+
     .file-finder-input {
       width: 95%;
       display: inline-block;
diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
index c9846103762abdd616d8fab37ed029932e6e5bab..3fa7fa3d7e3fc47bfe63137c7d5e86549c92a03c 100644
--- a/app/assets/stylesheets/pages/xterm.scss
+++ b/app/assets/stylesheets/pages/xterm.scss
@@ -23,15 +23,19 @@
   .term-bold {
     font-weight: bold;
   }
+
   .term-italic {
     font-style: italic;
   }
+
   .term-conceal {
     visibility: hidden;
   }
+
   .term-underline {
     text-decoration: underline;
   }
+
   .term-cross {
     text-decoration: line-through;
   }
@@ -39,48 +43,63 @@
   .term-fg-black {
     color: $black;
   }
+
   .term-fg-red {
     color: $red;
   }
+
   .term-fg-green {
     color: $green;
   }
+
   .term-fg-yellow {
     color: $yellow;
   }
+
   .term-fg-blue {
     color: $blue;
   }
+
   .term-fg-magenta {
     color: $magenta;
   }
+
   .term-fg-cyan {
     color: $cyan;
   }
+
   .term-fg-white {
     color: $white;
   }
+
   .term-fg-l-black {
     color: $l-black;
   }
+
   .term-fg-l-red {
     color: $l-red;
   }
+
   .term-fg-l-green {
     color: $l-green;
   }
+
   .term-fg-l-yellow {
     color: $l-yellow;
   }
+
   .term-fg-l-blue {
     color: $l-blue;
   }
+
   .term-fg-l-magenta {
     color: $l-magenta;
   }
+
   .term-fg-l-cyan {
     color: $l-cyan;
   }
+
   .term-fg-l-white {
     color: $l-white;
   }
@@ -88,818 +107,1087 @@
   .term-bg-black {
     background-color: $black;
   }
+
   .term-bg-red {
     background-color: $red;
   }
+
   .term-bg-green {
     background-color: $green;
   }
+
   .term-bg-yellow {
     background-color: $yellow;
   }
+
   .term-bg-blue {
     background-color: $blue;
   }
+
   .term-bg-magenta {
     background-color: $magenta;
   }
+
   .term-bg-cyan {
     background-color: $cyan;
   }
+
   .term-bg-white {
     background-color: $white;
   }
+
   .term-bg-l-black {
     background-color: $l-black;
   }
+
   .term-bg-l-red {
     background-color: $l-red;
   }
+
   .term-bg-l-green {
     background-color: $l-green;
   }
+
   .term-bg-l-yellow {
     background-color: $l-yellow;
   }
+
   .term-bg-l-blue {
     background-color: $l-blue;
   }
+
   .term-bg-l-magenta {
     background-color: $l-magenta;
   }
+
   .term-bg-l-cyan {
     background-color: $l-cyan;
   }
+
   .term-bg-l-white {
     background-color: $l-white;
   }
 
-
   .xterm-fg-0 {
     color: #000;
   }
+
   .xterm-fg-1 {
     color: #800000;
   }
+
   .xterm-fg-2 {
     color: #008000;
   }
+
   .xterm-fg-3 {
     color: #808000;
   }
+
   .xterm-fg-4 {
     color: #000080;
   }
+
   .xterm-fg-5 {
     color: #800080;
   }
+
   .xterm-fg-6 {
     color: #008080;
   }
+
   .xterm-fg-7 {
     color: #c0c0c0;
   }
+
   .xterm-fg-8 {
     color: #808080;
   }
+
   .xterm-fg-9 {
     color: #f00;
   }
+
   .xterm-fg-10 {
     color: #0f0;
   }
+
   .xterm-fg-11 {
     color: #ff0;
   }
+
   .xterm-fg-12 {
     color: #00f;
   }
+
   .xterm-fg-13 {
     color: #f0f;
   }
+
   .xterm-fg-14 {
     color: #0ff;
   }
+
   .xterm-fg-15 {
     color: #fff;
   }
+
   .xterm-fg-16 {
     color: #000;
   }
+
   .xterm-fg-17 {
     color: #00005f;
   }
+
   .xterm-fg-18 {
     color: #000087;
   }
+
   .xterm-fg-19 {
     color: #0000af;
   }
+
   .xterm-fg-20 {
     color: #0000d7;
   }
+
   .xterm-fg-21 {
     color: #00f;
   }
+
   .xterm-fg-22 {
     color: #005f00;
   }
+
   .xterm-fg-23 {
     color: #005f5f;
   }
+
   .xterm-fg-24 {
     color: #005f87;
   }
+
   .xterm-fg-25 {
     color: #005faf;
   }
+
   .xterm-fg-26 {
     color: #005fd7;
   }
+
   .xterm-fg-27 {
     color: #005fff;
   }
+
   .xterm-fg-28 {
     color: #008700;
   }
+
   .xterm-fg-29 {
     color: #00875f;
   }
+
   .xterm-fg-30 {
     color: #008787;
   }
+
   .xterm-fg-31 {
     color: #0087af;
   }
+
   .xterm-fg-32 {
     color: #0087d7;
   }
+
   .xterm-fg-33 {
     color: #0087ff;
   }
+
   .xterm-fg-34 {
     color: #00af00;
   }
+
   .xterm-fg-35 {
     color: #00af5f;
   }
+
   .xterm-fg-36 {
     color: #00af87;
   }
+
   .xterm-fg-37 {
     color: #00afaf;
   }
+
   .xterm-fg-38 {
     color: #00afd7;
   }
+
   .xterm-fg-39 {
     color: #00afff;
   }
+
   .xterm-fg-40 {
     color: #00d700;
   }
+
   .xterm-fg-41 {
     color: #00d75f;
   }
+
   .xterm-fg-42 {
     color: #00d787;
   }
+
   .xterm-fg-43 {
     color: #00d7af;
   }
+
   .xterm-fg-44 {
     color: #00d7d7;
   }
+
   .xterm-fg-45 {
     color: #00d7ff;
   }
+
   .xterm-fg-46 {
     color: #0f0;
   }
+
   .xterm-fg-47 {
     color: #00ff5f;
   }
+
   .xterm-fg-48 {
     color: #00ff87;
   }
+
   .xterm-fg-49 {
     color: #00ffaf;
   }
+
   .xterm-fg-50 {
     color: #00ffd7;
   }
+
   .xterm-fg-51 {
     color: #0ff;
   }
+
   .xterm-fg-52 {
     color: #5f0000;
   }
+
   .xterm-fg-53 {
     color: #5f005f;
   }
+
   .xterm-fg-54 {
     color: #5f0087;
   }
+
   .xterm-fg-55 {
     color: #5f00af;
   }
+
   .xterm-fg-56 {
     color: #5f00d7;
   }
+
   .xterm-fg-57 {
     color: #5f00ff;
   }
+
   .xterm-fg-58 {
     color: #5f5f00;
   }
+
   .xterm-fg-59 {
     color: #5f5f5f;
   }
+
   .xterm-fg-60 {
     color: #5f5f87;
   }
+
   .xterm-fg-61 {
     color: #5f5faf;
   }
+
   .xterm-fg-62 {
     color: #5f5fd7;
   }
+
   .xterm-fg-63 {
     color: #5f5fff;
   }
+
   .xterm-fg-64 {
     color: #5f8700;
   }
+
   .xterm-fg-65 {
     color: #5f875f;
   }
+
   .xterm-fg-66 {
     color: #5f8787;
   }
+
   .xterm-fg-67 {
     color: #5f87af;
   }
+
   .xterm-fg-68 {
     color: #5f87d7;
   }
+
   .xterm-fg-69 {
     color: #5f87ff;
   }
+
   .xterm-fg-70 {
     color: #5faf00;
   }
+
   .xterm-fg-71 {
     color: #5faf5f;
   }
+
   .xterm-fg-72 {
     color: #5faf87;
   }
+
   .xterm-fg-73 {
     color: #5fafaf;
   }
+
   .xterm-fg-74 {
     color: #5fafd7;
   }
+
   .xterm-fg-75 {
     color: #5fafff;
   }
+
   .xterm-fg-76 {
     color: #5fd700;
   }
+
   .xterm-fg-77 {
     color: #5fd75f;
   }
+
   .xterm-fg-78 {
     color: #5fd787;
   }
+
   .xterm-fg-79 {
     color: #5fd7af;
   }
+
   .xterm-fg-80 {
     color: #5fd7d7;
   }
+
   .xterm-fg-81 {
     color: #5fd7ff;
   }
+
   .xterm-fg-82 {
     color: #5fff00;
   }
+
   .xterm-fg-83 {
     color: #5fff5f;
   }
+
   .xterm-fg-84 {
     color: #5fff87;
   }
+
   .xterm-fg-85 {
     color: #5fffaf;
   }
+
   .xterm-fg-86 {
     color: #5fffd7;
   }
+
   .xterm-fg-87 {
     color: #5fffff;
   }
+
   .xterm-fg-88 {
     color: #870000;
   }
+
   .xterm-fg-89 {
     color: #87005f;
   }
+
   .xterm-fg-90 {
     color: #870087;
   }
+
   .xterm-fg-91 {
     color: #8700af;
   }
+
   .xterm-fg-92 {
     color: #8700d7;
   }
+
   .xterm-fg-93 {
     color: #8700ff;
   }
+
   .xterm-fg-94 {
     color: #875f00;
   }
+
   .xterm-fg-95 {
     color: #875f5f;
   }
+
   .xterm-fg-96 {
     color: #875f87;
   }
+
   .xterm-fg-97 {
     color: #875faf;
   }
+
   .xterm-fg-98 {
     color: #875fd7;
   }
+
   .xterm-fg-99 {
     color: #875fff;
   }
+
   .xterm-fg-100 {
     color: #878700;
   }
+
   .xterm-fg-101 {
     color: #87875f;
   }
+
   .xterm-fg-102 {
     color: #878787;
   }
+
   .xterm-fg-103 {
     color: #8787af;
   }
+
   .xterm-fg-104 {
     color: #8787d7;
   }
+
   .xterm-fg-105 {
     color: #8787ff;
   }
+
   .xterm-fg-106 {
     color: #87af00;
   }
+
   .xterm-fg-107 {
     color: #87af5f;
   }
+
   .xterm-fg-108 {
     color: #87af87;
   }
+
   .xterm-fg-109 {
     color: #87afaf;
   }
+
   .xterm-fg-110 {
     color: #87afd7;
   }
+
   .xterm-fg-111 {
     color: #87afff;
   }
+
   .xterm-fg-112 {
     color: #87d700;
   }
+
   .xterm-fg-113 {
     color: #87d75f;
   }
+
   .xterm-fg-114 {
     color: #87d787;
   }
+
   .xterm-fg-115 {
     color: #87d7af;
   }
+
   .xterm-fg-116 {
     color: #87d7d7;
   }
+
   .xterm-fg-117 {
     color: #87d7ff;
   }
+
   .xterm-fg-118 {
     color: #87ff00;
   }
+
   .xterm-fg-119 {
     color: #87ff5f;
   }
+
   .xterm-fg-120 {
     color: #87ff87;
   }
+
   .xterm-fg-121 {
     color: #87ffaf;
   }
+
   .xterm-fg-122 {
     color: #87ffd7;
   }
+
   .xterm-fg-123 {
     color: #87ffff;
   }
+
   .xterm-fg-124 {
     color: #af0000;
   }
+
   .xterm-fg-125 {
     color: #af005f;
   }
+
   .xterm-fg-126 {
     color: #af0087;
   }
+
   .xterm-fg-127 {
     color: #af00af;
   }
+
   .xterm-fg-128 {
     color: #af00d7;
   }
+
   .xterm-fg-129 {
     color: #af00ff;
   }
+
   .xterm-fg-130 {
     color: #af5f00;
   }
+
   .xterm-fg-131 {
     color: #af5f5f;
   }
+
   .xterm-fg-132 {
     color: #af5f87;
   }
+
   .xterm-fg-133 {
     color: #af5faf;
   }
+
   .xterm-fg-134 {
     color: #af5fd7;
   }
+
   .xterm-fg-135 {
     color: #af5fff;
   }
+
   .xterm-fg-136 {
     color: #af8700;
   }
+
   .xterm-fg-137 {
     color: #af875f;
   }
+
   .xterm-fg-138 {
     color: #af8787;
   }
+
   .xterm-fg-139 {
     color: #af87af;
   }
+
   .xterm-fg-140 {
     color: #af87d7;
   }
+
   .xterm-fg-141 {
     color: #af87ff;
   }
+
   .xterm-fg-142 {
     color: #afaf00;
   }
+
   .xterm-fg-143 {
     color: #afaf5f;
   }
+
   .xterm-fg-144 {
     color: #afaf87;
   }
+
   .xterm-fg-145 {
     color: #afafaf;
   }
+
   .xterm-fg-146 {
     color: #afafd7;
   }
+
   .xterm-fg-147 {
     color: #afafff;
   }
+
   .xterm-fg-148 {
     color: #afd700;
   }
+
   .xterm-fg-149 {
     color: #afd75f;
   }
+
   .xterm-fg-150 {
     color: #afd787;
   }
+
   .xterm-fg-151 {
     color: #afd7af;
   }
+
   .xterm-fg-152 {
     color: #afd7d7;
   }
+
   .xterm-fg-153 {
     color: #afd7ff;
   }
+
   .xterm-fg-154 {
     color: #afff00;
   }
+
   .xterm-fg-155 {
     color: #afff5f;
   }
+
   .xterm-fg-156 {
     color: #afff87;
   }
+
   .xterm-fg-157 {
     color: #afffaf;
   }
+
   .xterm-fg-158 {
     color: #afffd7;
   }
+
   .xterm-fg-159 {
     color: #afffff;
   }
+
   .xterm-fg-160 {
     color: #d70000;
   }
+
   .xterm-fg-161 {
     color: #d7005f;
   }
+
   .xterm-fg-162 {
     color: #d70087;
   }
+
   .xterm-fg-163 {
     color: #d700af;
   }
+
   .xterm-fg-164 {
     color: #d700d7;
   }
+
   .xterm-fg-165 {
     color: #d700ff;
   }
+
   .xterm-fg-166 {
     color: #d75f00;
   }
+
   .xterm-fg-167 {
     color: #d75f5f;
   }
+
   .xterm-fg-168 {
     color: #d75f87;
   }
+
   .xterm-fg-169 {
     color: #d75faf;
   }
+
   .xterm-fg-170 {
     color: #d75fd7;
   }
+
   .xterm-fg-171 {
     color: #d75fff;
   }
+
   .xterm-fg-172 {
     color: #d78700;
   }
+
   .xterm-fg-173 {
     color: #d7875f;
   }
+
   .xterm-fg-174 {
     color: #d78787;
   }
+
   .xterm-fg-175 {
     color: #d787af;
   }
+
   .xterm-fg-176 {
     color: #d787d7;
   }
+
   .xterm-fg-177 {
     color: #d787ff;
   }
+
   .xterm-fg-178 {
     color: #d7af00;
   }
+
   .xterm-fg-179 {
     color: #d7af5f;
   }
+
   .xterm-fg-180 {
     color: #d7af87;
   }
+
   .xterm-fg-181 {
     color: #d7afaf;
   }
+
   .xterm-fg-182 {
     color: #d7afd7;
   }
+
   .xterm-fg-183 {
     color: #d7afff;
   }
+
   .xterm-fg-184 {
     color: #d7d700;
   }
+
   .xterm-fg-185 {
     color: #d7d75f;
   }
+
   .xterm-fg-186 {
     color: #d7d787;
   }
+
   .xterm-fg-187 {
     color: #d7d7af;
   }
+
   .xterm-fg-188 {
     color: #d7d7d7;
   }
+
   .xterm-fg-189 {
     color: #d7d7ff;
   }
+
   .xterm-fg-190 {
     color: #d7ff00;
   }
+
   .xterm-fg-191 {
     color: #d7ff5f;
   }
+
   .xterm-fg-192 {
     color: #d7ff87;
   }
+
   .xterm-fg-193 {
     color: #d7ffaf;
   }
+
   .xterm-fg-194 {
     color: #d7ffd7;
   }
+
   .xterm-fg-195 {
     color: #d7ffff;
   }
+
   .xterm-fg-196 {
     color: #f00;
   }
+
   .xterm-fg-197 {
     color: #ff005f;
   }
+
   .xterm-fg-198 {
     color: #ff0087;
   }
+
   .xterm-fg-199 {
     color: #ff00af;
   }
+
   .xterm-fg-200 {
     color: #ff00d7;
   }
+
   .xterm-fg-201 {
     color: #f0f;
   }
+
   .xterm-fg-202 {
     color: #ff5f00;
   }
+
   .xterm-fg-203 {
     color: #ff5f5f;
   }
+
   .xterm-fg-204 {
     color: #ff5f87;
   }
+
   .xterm-fg-205 {
     color: #ff5faf;
   }
+
   .xterm-fg-206 {
     color: #ff5fd7;
   }
+
   .xterm-fg-207 {
     color: #ff5fff;
   }
+
   .xterm-fg-208 {
     color: #ff8700;
   }
+
   .xterm-fg-209 {
     color: #ff875f;
   }
+
   .xterm-fg-210 {
     color: #ff8787;
   }
+
   .xterm-fg-211 {
     color: #ff87af;
   }
+
   .xterm-fg-212 {
     color: #ff87d7;
   }
+
   .xterm-fg-213 {
     color: #ff87ff;
   }
+
   .xterm-fg-214 {
     color: #ffaf00;
   }
+
   .xterm-fg-215 {
     color: #ffaf5f;
   }
+
   .xterm-fg-216 {
     color: #ffaf87;
   }
+
   .xterm-fg-217 {
     color: #ffafaf;
   }
+
   .xterm-fg-218 {
     color: #ffafd7;
   }
+
   .xterm-fg-219 {
     color: #ffafff;
   }
+
   .xterm-fg-220 {
     color: #ffd700;
   }
+
   .xterm-fg-221 {
     color: #ffd75f;
   }
+
   .xterm-fg-222 {
     color: #ffd787;
   }
+
   .xterm-fg-223 {
     color: #ffd7af;
   }
+
   .xterm-fg-224 {
     color: #ffd7d7;
   }
+
   .xterm-fg-225 {
     color: #ffd7ff;
   }
+
   .xterm-fg-226 {
     color: #ff0;
   }
+
   .xterm-fg-227 {
     color: #ffff5f;
   }
+
   .xterm-fg-228 {
     color: #ffff87;
   }
+
   .xterm-fg-229 {
     color: #ffffaf;
   }
+
   .xterm-fg-230 {
     color: #ffffd7;
   }
+
   .xterm-fg-231 {
     color: #fff;
   }
+
   .xterm-fg-232 {
     color: #080808;
   }
+
   .xterm-fg-233 {
     color: #121212;
   }
+
   .xterm-fg-234 {
     color: #1c1c1c;
   }
+
   .xterm-fg-235 {
     color: #262626;
   }
+
   .xterm-fg-236 {
     color: #303030;
   }
+
   .xterm-fg-237 {
     color: #3a3a3a;
   }
+
   .xterm-fg-238 {
     color: #444;
   }
+
   .xterm-fg-239 {
     color: #4e4e4e;
   }
+
   .xterm-fg-240 {
     color: #585858;
   }
+
   .xterm-fg-241 {
     color: #626262;
   }
+
   .xterm-fg-242 {
     color: #6c6c6c;
   }
+
   .xterm-fg-243 {
     color: #767676;
   }
+
   .xterm-fg-244 {
     color: #808080;
   }
+
   .xterm-fg-245 {
     color: #8a8a8a;
   }
+
   .xterm-fg-246 {
     color: #949494;
   }
+
   .xterm-fg-247 {
     color: #9e9e9e;
   }
+
   .xterm-fg-248 {
     color: #a8a8a8;
   }
+
   .xterm-fg-249 {
     color: #b2b2b2;
   }
+
   .xterm-fg-250 {
     color: #bcbcbc;
   }
+
   .xterm-fg-251 {
     color: #c6c6c6;
   }
+
   .xterm-fg-252 {
     color: #d0d0d0;
   }
+
   .xterm-fg-253 {
     color: #dadada;
   }
+
   .xterm-fg-254 {
     color: #e4e4e4;
   }
+
   .xterm-fg-255 {
     color: #eee;
   }
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index b3455e04c29fc97e03a52032ee7c694e0ecc54c0..705824502eb971a698e9df4a050751cda66c61c9 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -45,6 +45,10 @@ class ApplicationController < ActionController::Base
     redirect_to request.referer.present? ? :back : default, options
   end
 
+  def not_found
+    render_404
+  end
+
   protected
 
   # This filter handles both private tokens and personal access tokens
diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb
index 7a7475a734503ce50763314fa354498fbfc4e013..ae060abee5c6af949f4a70c9d919280130ca47d8 100644
--- a/app/controllers/projects/group_links_controller.rb
+++ b/app/controllers/projects/group_links_controller.rb
@@ -1,6 +1,7 @@
 class Projects::GroupLinksController < Projects::ApplicationController
   layout 'project_settings'
   before_action :authorize_admin_project!
+  before_action :authorize_admin_project_member!, only: [:update]
 
   def index
     @group_links = project.project_group_links.all
@@ -27,9 +28,26 @@ class Projects::GroupLinksController < Projects::ApplicationController
     redirect_to namespace_project_group_links_path(project.namespace, project)
   end
 
+  def update
+    @group_link = @project.project_group_links.find(params[:id])
+
+    @group_link.update_attributes(group_link_params)
+  end
+
   def destroy
     project.project_group_links.find(params[:id]).destroy
 
-    redirect_to namespace_project_group_links_path(project.namespace, project)
+    respond_to do |format|
+      format.html do
+        redirect_to namespace_project_group_links_path(project.namespace, project)
+      end
+      format.js { head :ok }
+    end
+  end
+
+  protected
+
+  def group_link_params
+    params.require(:group_link).permit(:group_access, :expires_at)
   end
 end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 869d96b86f45cd0769d42460009eeb22fdc467d2..9207c954335cb94cc1965b150a2eeebaa25bc850 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -10,7 +10,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   before_action :module_enabled
   before_action :merge_request, only: [
     :edit, :update, :show, :diffs, :commits, :conflicts, :builds, :pipelines, :merge, :merge_check,
-    :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
+    :ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
   ]
   before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds, :pipelines]
   before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :builds, :pipelines]
@@ -403,6 +403,30 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     render json: response
   end
 
+  def ci_environments_status
+    environments =
+      begin
+        @merge_request.environments.map do |environment|
+          next unless can?(current_user, :read_environment, environment)
+
+          project = environment.project
+          deployment = environment.first_deployment_for(@merge_request.diff_head_commit)
+
+          {
+            id: environment.id,
+            name: environment.name,
+            url: namespace_project_environment_path(project.namespace, project, environment),
+            external_url: environment.external_url,
+            external_url_formatted: environment.formatted_external_url,
+            deployed_at: deployment.try(:created_at),
+            deployed_at_formatted: deployment.try(:formatted_deployment_time)
+          }
+        end.compact
+      end
+
+    render json: environments
+  end
+
   protected
 
   def selected_target_project
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index f56b256984ba27347a49ab099c28edf5cd3080e5..37a86ed0523e422df930d41e42c5ac18ae264f94 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -5,34 +5,23 @@ class Projects::ProjectMembersController < Projects::ApplicationController
   before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access]
 
   def index
+    @group_links = @project.project_group_links
+
     @project_members = @project.project_members
     @project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project)
 
     if params[:search].present?
       users = @project.users.search(params[:search]).to_a
       @project_members = @project_members.where(user_id: users)
-    end
-
-    @project_members = @project_members.order('access_level DESC')
-
-    @group = @project.group
-
-    if @group
-      @group_members = @group.group_members
-      @group_members = @group_members.non_invite unless can?(current_user, :admin_group, @group)
-
-      if params[:search].present?
-        users = @group.users.search(params[:search]).to_a
-        @group_members = @group_members.where(user_id: users)
-      end
 
-      @group_members = @group_members.order('access_level DESC')
+      @group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id))
     end
 
+    @project_members = @project_members.order(access_level: :desc).page(params[:page])
+
     @requesters = AccessRequestsFinder.new(@project).execute(current_user)
 
     @project_member = @project.project_members.new
-    @project_group_links = @project.project_group_links
   end
 
   def create
@@ -43,6 +32,21 @@ class Projects::ProjectMembersController < Projects::ApplicationController
       current_user: current_user
     )
 
+    if params[:group_ids].present?
+      group_ids = params[:group_ids].split(',')
+      groups = Group.where(id: group_ids)
+
+      groups.each do |group|
+        next unless can?(current_user, :read_group, group)
+
+        project.project_group_links.create(
+          group: group,
+          group_access: params[:access_level],
+          expires_at: params[:expires_at]
+        )
+      end
+    end
+
     redirect_to namespace_project_project_members_path(@project.namespace, @project)
   end
 
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 838ecc837e4f0ea6ad0ca09ae8696c5c15e61b0e..6a881b271d7ea1d508981be718b0b19a3f7fe715 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,6 +1,6 @@
 class UsersController < ApplicationController
   skip_before_action :authenticate_user!
-  before_action :user
+  before_action :user, except: [:exists]
   before_action :authorize_read_user!, only: [:show]
 
   def show
@@ -85,6 +85,10 @@ class UsersController < ApplicationController
     render 'calendar_activities', layout: false
   end
 
+  def exists
+    render json: { exists: Namespace.where(path: params[:username].downcase).any? }
+  end
+
   private
 
   def authorize_read_user!
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5dbf66173de79101b39dee6034ec14db0ac88d68..87475119b23766e41d94996669e36c68002c7aa1 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -1,6 +1,7 @@
 module Ci
   class Build < CommitStatus
     include TokenAuthenticatable
+    include AfterCommitQueue
 
     belongs_to :runner, class_name: 'Ci::Runner'
     belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
@@ -75,25 +76,20 @@ module Ci
 
     state_machine :status do
       after_transition pending: :running do |build|
-        build.execute_hooks
+        build.run_after_commit do
+          BuildHooksWorker.perform_async(id)
+        end
       end
 
       after_transition any => [:success, :failed, :canceled] do |build|
-        build.update_coverage
-        build.execute_hooks
+        build.run_after_commit do
+          BuildFinishedWorker.perform_async(id)
+        end
       end
 
       after_transition any => [:success] do |build|
-        if build.environment.present?
-          service = CreateDeploymentService.new(
-            build.project, build.user,
-            environment: build.environment,
-            sha: build.sha,
-            ref: build.ref,
-            tag: build.tag,
-            options: build.options.to_h[:environment],
-            variables: build.variables)
-          service.execute(build)
+        build.run_after_commit do
+          BuildSuccessWorker.perform_async(id)
         end
       end
     end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 957f6755b2e3c26b0fbd63c0c66bb6bd418dddfc..4fdb5fef4fbe72b13b465d0b35a98e48e2f2ca95 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -76,7 +76,11 @@ module Ci
       end
 
       after_transition do |pipeline, transition|
-        pipeline.execute_hooks unless transition.loopback?
+        next if transition.loopback?
+
+        pipeline.run_after_commit do
+          PipelineHooksWorker.perform_async(id)
+        end
       end
     end
 
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index f63cc179b9e6bc8d7ee1d74964ece2901964279f..3d9902d496e837741a4789a00379bf32c6828dcf 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -84,6 +84,10 @@ class Deployment < ActiveRecord::Base
       take
   end
 
+  def formatted_deployment_time
+    created_at.to_time.in_time_zone.to_s(:medium)
+  end
+
   private
 
   def ref_path
diff --git a/app/models/environment.rb b/app/models/environment.rb
index f0f3ee23223e2bdb33a3ee2246a4e6d97fdf07f9..d970bc0a0052c9bc18f37b76d2b27730efc60db8 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -48,7 +48,22 @@ class Environment < ActiveRecord::Base
     self.name == "production"
   end
 
+  def first_deployment_for(commit)
+    ref = project.repository.ref_name_for_sha(ref_path, commit.sha)
+
+    return nil unless ref
+
+    deployment_id = ref.split('/').last
+    deployments.find(deployment_id)
+  end
+
   def ref_path
     "refs/environments/#{Shellwords.shellescape(name)}"
   end
+
+  def formatted_external_url
+    return nil unless external_url
+
+    external_url.gsub(/\A.*?:\/\//, '')
+  end
 end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index a743bf313ae98293a546412995a73e1351ae00f4..5ccfe11a2a29a12e954791a9677febe71074c969 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -688,12 +688,15 @@ class MergeRequest < ActiveRecord::Base
   def environments
     return [] unless diff_head_commit
 
-    environments = source_project.environments_for(
-      source_branch, diff_head_commit)
-    environments += target_project.environments_for(
-      target_branch, diff_head_commit, with_tags: true)
-
-    environments.uniq
+    @environments ||=
+      begin
+        environments = source_project.environments_for(
+          source_branch, diff_head_commit)
+        environments += target_project.environments_for(
+          target_branch, diff_head_commit, with_tags: true)
+
+        environments.uniq
+      end
   end
 
   def state_human_name
diff --git a/app/models/project.rb b/app/models/project.rb
index 758927edd5c2076a59b6731bbf68dbb5f983eb27..db3088677d8d2d9f6d5ca21bfb73a230026d5287 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -490,7 +490,7 @@ class Project < ActiveRecord::Base
   end
 
   def import_url
-    if import_data && super
+    if import_data && super.present?
       import_url = Gitlab::UrlSanitizer.new(super, credentials: import_data.credentials)
       import_url.full_url
     else
@@ -829,11 +829,6 @@ class Project < ActiveRecord::Base
     end
   end
 
-  def update_merge_requests(oldrev, newrev, ref, user)
-    MergeRequests::RefreshService.new(self, user).
-      execute(oldrev, newrev, ref)
-  end
-
   def valid_repo?
     repository.exists?
   rescue
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 608c99eed46e1ad352c30c5bd0f9c3440f1334fc..72e473871fab899450ee5f4200882000700dc19b 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -719,6 +719,14 @@ class Repository
     end
   end
 
+  def ref_name_for_sha(ref_path, sha)
+    args = %W(#{Gitlab.config.git.bin_path} for-each-ref --count=1 #{ref_path} --contains #{sha})
+
+    # Not found -> ["", 0]
+    # Found -> ["b8d95eb4969eefacb0a58f6a28f6803f8070e7b9 commit\trefs/environments/production/77\n", 0]
+    Gitlab::Popen.popen(args, path_to_repo).first.split.last
+  end
+
   def refs_contains_sha(ref_type, sha)
     args = %W(#{Gitlab.config.git.bin_path} #{ref_type} --contains #{sha})
     names = Gitlab::Popen.popen(args, path_to_repo).first
diff --git a/app/services/create_deployment_service.rb b/app/services/create_deployment_service.rb
index 799ad3e1bd0f40cc6ac5d8137e1a65e9ece4fa21..ff9a8310a8c64ab83915ae9e8a6078fab5b3ca08 100644
--- a/app/services/create_deployment_service.rb
+++ b/app/services/create_deployment_service.rb
@@ -2,25 +2,35 @@ require_relative 'base_service'
 
 class CreateDeploymentService < BaseService
   def execute(deployable = nil)
-    environment = find_or_create_environment
+    return unless executable?
 
-    deployment = project.deployments.create(
-      environment: environment,
+    ActiveRecord::Base.transaction do
+      @deployable = deployable
+      @environment = prepare_environment
+
+      deploy.tap do |deployment|
+        deployment.update_merge_request_metrics!
+      end
+    end
+  end
+
+  private
+
+  def executable?
+    project && name.present?
+  end
+
+  def deploy
+    project.deployments.create(
+      environment: @environment,
       ref: params[:ref],
       tag: params[:tag],
       sha: params[:sha],
       user: current_user,
-      deployable: deployable
-    )
-
-    deployment.update_merge_request_metrics!
-
-    deployment
+      deployable: @deployable)
   end
 
-  private
-
-  def find_or_create_environment
+  def prepare_environment
     project.environments.find_or_create_by(name: expanded_name) do |environment|
       environment.external_url = expanded_url
     end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index c499427605adb41c78f8b517e48c69736776f439..e8415862de51be3daf703dc1e5584f6b2a961188 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -63,13 +63,12 @@ class GitPushService < BaseService
   protected
 
   def update_merge_requests
-    @project.update_merge_requests(params[:oldrev], params[:newrev], params[:ref], current_user)
+    UpdateMergeRequestsWorker.perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref])
 
     EventCreateService.new.push(@project, current_user, build_push_data)
-    SystemHooksService.new.execute_hooks(build_push_data_system_hook.dup, :push_hooks)
     @project.execute_hooks(build_push_data.dup, :push_hooks)
     @project.execute_services(build_push_data.dup, :push_hooks)
-    Ci::CreatePipelineService.new(project, current_user, build_push_data).execute
+    Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute
     ProjectCacheWorker.perform_async(@project.id)
   end
 
@@ -148,16 +147,6 @@ class GitPushService < BaseService
       push_commits)
   end
 
-  def build_push_data_system_hook
-    @push_data_system ||= Gitlab::DataBuilder::Push.build(
-      @project,
-      current_user,
-      params[:oldrev],
-      params[:newrev],
-      params[:ref],
-      [])
-  end
-
   def push_to_existing_branch?
     # Return if this is not a push to a branch (e.g. new commits)
     Gitlab::Git.branch_ref?(params[:ref]) && !Gitlab::Git.blank_ref?(params[:oldrev])
diff --git a/app/views/admin/appearances/preview.html.haml b/app/views/admin/appearances/preview.html.haml
index 6c51639b8405f757ec18105023bd40d0b3e341f9..acbe17036f78333b2fa36a938c37b1ca3f91721d 100644
--- a/app/views/admin/appearances/preview.html.haml
+++ b/app/views/admin/appearances/preview.html.haml
@@ -1,9 +1,12 @@
-- page_title "Preview | Appearance"
+= render 'devise/shared/tab_single', tab_title: 'Sign in preview'
 .login-box
-  .login-heading
-    %h3 Existing user? Sign in
-  %form
-    = text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
-    = password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
-    = button_tag "Sign in", class: "btn-create btn"
+  %form.show-gl-field-errors
+    .form-group
+      = label_tag :login
+      = text_field_tag :login, nil, class: "form-control top", title: 'Please provide your username or email address.'
+    .form-group
+      = label_tag :password
+      = password_field_tag :password, nil, class: "form-control bottom", title: 'This field is required.'
+    .form-group
+      = button_tag "Sign in", class: "btn-create btn"
 
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index a5e82e55cc136854f588f91cdacf51f4e508cf4c..10fea1996aa2337a8e87924c9794bab30c69cb55 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -71,7 +71,7 @@
 
   .col-md-6
     %h4 Recent builds served by this Runner
-    %table.table.builds.runner-builds
+    %table.table.ci-table.runner-builds
       %thead
         %tr
           %th Build
diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml
index 970ba1471118bfb89a7e2fa6545ede4a72092531..5d25dd398d6a9741b6f38aa87f8f47c047d49877 100644
--- a/app/views/devise/confirmations/new.html.haml
+++ b/app/views/devise/confirmations/new.html.haml
@@ -1,14 +1,14 @@
+= render 'devise/shared/tab_single', tab_title: 'Resend confirmation instructions'
 .login-box
-  .login-heading
-    %h3 Resend confirmation instructions
   .login-body
-    = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f|
+    = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: 'show-gl-field-errors' }) do |f|
       .devise-errors
         = devise_error_messages!
-      .clearfix.append-bottom-20
-        = f.email_field :email, placeholder: 'Email', class: "form-control", required: true
+      .form-group
+        = f.label :email
+        = f.email_field :email, class: "form-control", required: true, title: 'Please provide a valid email address.'
       .clearfix
-        = f.submit "Resend confirmation instructions", class: 'btn btn-success'
+        = f.submit "Resend", class: 'btn btn-success'
 
 .clearfix.prepend-top-20
   = render 'devise/shared/sign_in_link'
diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
index 56048e99c17f834100138efe080de281fb875821..b518fae7c9598b2c722e45b3f8e4e817853123c3 100644
--- a/app/views/devise/passwords/edit.html.haml
+++ b/app/views/devise/passwords/edit.html.haml
@@ -1,19 +1,21 @@
+= render 'devise/shared/tab_single', tab_title:'Change your password'
 .login-box
-  .login-heading
-    %h3 Change your password
   .login-body
-    = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f|
+    = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'show-gl-field-errors' }) do |f|
       .devise-errors
         = devise_error_messages!
       = f.hidden_field :reset_password_token
-      %div
-        = f.password_field :password, class: "form-control top", placeholder: "New password", required: true
-      %div
-        = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true
+      .form-group
+        = f.label 'New password', for: :password
+        = f.password_field :password, class: "form-control top",  required: true, title: 'This field is required'
+      .form-group
+        = f.label 'Confirm new password', for: :password_confirmation
+        = f.password_field :password_confirmation, class: "form-control bottom", title: 'This field is required', required: true
       .clearfix
         = f.submit "Change your password", class: "btn btn-primary"
 
 .clearfix.prepend-top-20
   %p
-    = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
-  = render 'devise/shared/sign_in_link'
+    %span.light Didn't receive a confirmation email?
+    = link_to "Request a new one", new_confirmation_path(resource_name)
+= render 'devise/shared/sign_in_link'
diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml
index 535e85869e54564a5b08eb24c7b836521405873a..1fcfd06419a55679799f8045c35c9059262acc7b 100644
--- a/app/views/devise/passwords/new.html.haml
+++ b/app/views/devise/passwords/new.html.haml
@@ -1,12 +1,12 @@
+= render 'devise/shared/tab_single', tab_title: 'Reset Password'
 .login-box
-  .login-heading
-    %h3 Reset password
   .login-body
-    = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f|
+    = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'show-gl-field-errors' }) do |f|
       .devise-errors
         = devise_error_messages!
-      .clearfix.append-bottom-20
-        = f.email_field :email, placeholder: "Email",  class: "form-control", required: true, value: params[:user_email], autofocus: true
+      .form-group
+        = f.label :email
+        = f.email_field :email, class: "form-control", required: true, value: params[:user_email], autofocus: true, title: 'Please provide a valid email address.'
       .clearfix
         = f.submit "Reset password", class: "btn-primary btn"
 
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 9f5520603cdbe0fb6c14ee0c2428c05487cb82cc..a96b579c59329c1c20b11ef9680e342799ed9208 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -1,6 +1,10 @@
-= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f|
-  = f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off"
-  = f.password_field :password, class: "form-control bottom", placeholder: "Password"
+= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user show-gl-field-errors', 'aria-live' => 'assertive'}) do |f|
+  %div.form-group
+    = f.label "Username or email", for: :login
+    = f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required."
+  %div.form-group
+    = f.label :password
+    = f.password_field :password, class: "form-control bottom", required: true, title: "This field is required."
   - if devise_mapping.rememberable?
     .remember-me.checkbox
       %label{for: "user_remember_me"}
@@ -8,5 +12,5 @@
         %span Remember me
       .pull-right
         = link_to "Forgot your password?", new_password_path(resource_name)
-  %div
+  %div.submit-container
     = f.submit "Sign in", class: "btn btn-save"
diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml
index b7d3acac2b152e5c90c5e0e256fb380e317e8b21..e82a08cdb0c2515e35408405ce50613327cbe157 100644
--- a/app/views/devise/sessions/_new_crowd.html.haml
+++ b/app/views/devise/sessions/_new_crowd.html.haml
@@ -1,6 +1,10 @@
-= form_tag(omniauth_authorize_path(:user, :crowd), id: 'new_crowd_user' ) do
-  = text_field_tag :username, nil, {class: "form-control top", placeholder: "Username", autofocus: "autofocus"}
-  = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
+= form_tag(omniauth_authorize_path(:user, :crowd), id: 'new_crowd_user', class: 'show-gl-field-errors') do
+  .form-group
+    = label_tag 'Username or email', for: :username
+    = text_field_tag :username, nil, {class: "form-control top", title: "This field is required", autofocus: "autofocus", required: true }
+  .form-group
+    = label_tag :password
+    = password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true }
   - if devise_mapping.rememberable?
     .remember-me.checkbox
       %label{for: "remember_me"}
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index 2ef383960f4189d60739104be039faf64d365950..b26efbb453518aba81c4c79d6f389a85463a4dde 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -1,6 +1,10 @@
-= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user') do
-  = text_field_tag :username, nil, {class: "form-control top", placeholder: "#{server['label']} Login", autofocus: "autofocus"}
-  = password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
+= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user', class: "show-gl-field-errors") do
+  .form-group
+    = label_tag "#{server['label']} Login", for: :username
+    = text_field_tag :username, nil, {class: "form-control top", title: "This field is required.", autofocus: "autofocus", required: true }
+  .form-group
+    = label_tag :password
+    = password_field_tag :password, nil, { class: "form-control bottom", title: "This field is required.", required: true }
   - if devise_mapping.rememberable?
     .remember-me.checkbox
       %label{for: "remember_me"}
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index 28194506accff5f5eeef336c9a5a3db747c0a429..fa8e7979461dc8c25b77ecb3ba5a6ec6c3afc486 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -1,19 +1,22 @@
 - page_title "Sign in"
 %div
-  - if signin_enabled? || ldap_enabled? || crowd_enabled?
-    = render 'devise/shared/signin_box'
+  - if form_based_providers.any?
+    = render 'devise/shared/tabs_ldap'
+  - else
+    = render 'devise/shared/tabs_normal'
+  .tab-content
+    - if signin_enabled? || ldap_enabled? || crowd_enabled?
+      = render 'devise/shared/signin_box'
 
-  -# Omniauth fits between signin/ldap signin and signup and does not have a surrounding box
-  - if omniauth_enabled? && devise_mapping.omniauthable? && button_based_providers_enabled?
-    .clearfix.prepend-top-20
-      = render 'devise/shared/omniauth_box'
-
-  -# Signup only makes sense if you can also sign-in
-  - if signin_enabled? && signup_enabled?
-    .prepend-top-20
+    -# Signup only makes sense if you can also sign-in
+    - if signin_enabled? && signup_enabled?
       = render 'devise/shared/signup_box'
 
   -# Show a message if none of the mechanisms above are enabled
   - if !signin_enabled? && !ldap_enabled? && !(omniauth_enabled? && devise_mapping.omniauthable?)
     %div
       No authentication methods configured.
+
+  - if omniauth_enabled? && devise_mapping.omniauthable? && button_based_providers_enabled?
+    .clearfix
+      = render 'devise/shared/omniauth_box'
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index e623f7cff889f99216ad8fd42d39f66967f67021..0e865b807c1bb07567a1426024bc828a116a85de 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -3,20 +3,19 @@
     = page_specific_javascript_tag('u2f.js')
 
 %div
+  = render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication'
   .login-box
-    .login-heading
-      %h3 Two-Factor Authentication
     .login-body
       - if @user.two_factor_otp_enabled?
-        %h5 Authenticate via Two-Factor App
-        = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
+        = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user show-gl-field-errors' }) do |f|
           - resource_params = params[resource_name].presence || params
           = f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
-          = f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-Factor Authentication code', required: true, autofocus: true, autocomplete: 'off'
-          %p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
-          .prepend-top-20
-            = f.submit "Verify code", class: "btn btn-save"
+          .form-group
+            = f.label 'Two-Factor Authentication code', name:  :otp_attempt
+            = f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.'
+            %p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
+            .prepend-top-20
+              = f.submit "Verify code", class: "btn btn-save"
 
       - if @user.two_factor_u2f_enabled?
-        %hr
         = render "u2f/authenticate", locals: { params: params, resource: resource, resource_name: resource_name }
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index 2e7da2747d0a421f86a995dc1844a7d467842bf0..8908b64cdac9f7ccc7efae71d8c11008134d5151 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -1,8 +1,9 @@
-%p
-  %span.light
-    Sign in with &nbsp;
-  - providers = enabled_button_based_providers
-  - providers.each do |provider|
+%div.omniauth-container
+  %p
     %span.light
-      - has_icon = provider_has_icon?(provider)
-      = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn'), "data-no-turbolink" => "true"
+      Sign in with &nbsp;
+    - providers = enabled_button_based_providers
+    - providers.each do |provider|
+      %span.light
+        - has_icon = provider_has_icon?(provider)
+        = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn'), "data-no-turbolink" => "true"
diff --git a/app/views/devise/shared/_sign_in_link.html.haml b/app/views/devise/shared/_sign_in_link.html.haml
index fafc4b82f5394cf258ddc2fda1310b153b9a3537..289bf40f3de41113aec2247385b146a228dde821 100644
--- a/app/views/devise/shared/_sign_in_link.html.haml
+++ b/app/views/devise/shared/_sign_in_link.html.haml
@@ -1,5 +1,4 @@
 %p
   %span.light
     Already have login and password?
-  %strong
     = link_to "Sign in", new_session_path(resource_name)
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index 2c15e2c4891c0ab90e6b961a66deef3df24c143b..810dd5ab6875c84db4c0f0f3714d3c6520815ee0 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -1,32 +1,15 @@
-.login-box
-  - if signup_enabled?
-    .login-heading
-      %h3 Existing user? Sign in
-  - else
-    .login-heading
-      %h3 Sign in
+#login-pane.login-box{ role: 'tabpanel', class: 'tab-pane active' }
   .login-body
     - if form_based_providers.any?
-      %ul.nav-links
-        - if crowd_enabled?
-          %li.active
-            = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
-        - @ldap_servers.each_with_index do |server, i|
-          %li{class: (:active if i.zero? && !crowd_enabled?)}
-            = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
-        - if signin_enabled?
-          %li
-            = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
-      .tab-content
-        - if crowd_enabled?
-          %div.tab-pane.active{id: "tab-crowd"}
-            = render 'devise/sessions/new_crowd'
-        - @ldap_servers.each_with_index do |server, i|
-          %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)}
-            = render 'devise/sessions/new_ldap', server: server
-        - if signin_enabled?
-          %div#tab-signin.tab-pane
-            = render 'devise/sessions/new_base'
+      - if crowd_enabled?
+        %div.tab-pane.active{id: "tab-crowd"}
+          = render 'devise/sessions/new_crowd'
+      - @ldap_servers.each_with_index do |server, i|
+        %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)}
+          = render 'devise/sessions/new_ldap', server: server
+      - if signin_enabled?
+        %div#tab-signin.tab-pane
+          = render 'devise/sessions/new_base'
 
     - elsif signin_enabled?
       = render 'devise/sessions/new_base'
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 905a8dbcd841ac80d82f56fe42916da0f53d7d85..d0bbcf3115e2cd084137f3270cd9633f5506d59d 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -1,29 +1,30 @@
-.login-box
-  - if signin_enabled?
-    .login-heading
-      %h3 New user? Create an account
-  - else
-    .login-heading
-      %h3 Create an account
+#register-pane.login-box{ role: 'tabpanel', class: 'tab-pane' }
   .login-body
-    = form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name)) do |f|
+    = form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user show-gl-field-errors", "aria-live" => "assertive" }) do |f|
       .devise-errors
         = devise_error_messages!
-      %div
-        = f.text_field :name, class: "form-control top", placeholder: "Name", required: true
-      %div
-        = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true
-      %div
-        = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
+      %div.form-group
+        = f.label :name
+        = f.text_field :name, class: "form-control top", required: true, title: "This field is required."
+      %div.username.form-group
+        = f.label :username
+        = f.text_field :username, class: "form-control middle no-gl-field-error", pattern: "[a-zA-Z0-9]+", required: true, title: 'Please create a username with only alphanumeric characters.'
+        %p.validation-error.hide Username is already taken.
+        %p.validation-success.hide Username is available.
+        %p.validation-pending.hide Checking username availability...
+      %div.form-group
+        = f.label :email
+        = f.email_field :email, class: "form-control middle", required: true, title: "Please provide a valid email address."
       .form-group.append-bottom-20#password-strength
-        = f.password_field :password, class: "form-control bottom", placeholder: "Password - minimum length #{@minimum_password_length} characters", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters"
+        = f.label :password
+        = f.password_field :password, class: "form-control bottom", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters."
+        %p.gl-field-hint Minimum length is #{@minimum_password_length} characters
       %div
       - if current_application_settings.recaptcha_enabled
         = recaptcha_tags
       %div
-        = f.submit "Sign up", class: "btn-create btn"
-
-.clearfix.prepend-top-20
+        = f.submit "Register", class: "btn-register btn"
+.clearfix.submit-container
   %p
     %span.light Didn't receive a confirmation email?
     = succeed '.' do
diff --git a/app/views/devise/shared/_tab_single.html.haml b/app/views/devise/shared/_tab_single.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..f943d25e41a78e7abca023fbaae3fb0ae107df5c
--- /dev/null
+++ b/app/views/devise/shared/_tab_single.html.haml
@@ -0,0 +1,3 @@
+%ul.nav-links.nav-tabs.new-session-tabs.single-tab
+  %li.active
+    %a= tab_title
diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..e276e91433ad6a3d25016456e6d5dea77e64bbe3
--- /dev/null
+++ b/app/views/devise/shared/_tabs_ldap.html.haml
@@ -0,0 +1,10 @@
+%ul.new-session-tabs.nav-links.nav-tabs
+  - if crowd_enabled?
+    %li.active
+      = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
+  - @ldap_servers.each_with_index do |server, i|
+    %li{class: (:active if i.zero? && !crowd_enabled?)}
+      = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
+  - if signin_enabled?
+    %li
+      = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..79b1d447a929cfad0f5df344e56a24f9f165e7fd
--- /dev/null
+++ b/app/views/devise/shared/_tabs_normal.html.haml
@@ -0,0 +1,5 @@
+%ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist'}
+  %li.active{ role: 'presentation' }
+    %a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab'} Sign in
+  %li{ role: 'presentation'}
+    %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab'} Register
diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml
index 49c087c0646e42112a6266225eb0ecf0aa225039..49b2f77111fc4c039b80a20146a8d031b67eeedb 100644
--- a/app/views/devise/unlocks/new.html.haml
+++ b/app/views/devise/unlocks/new.html.haml
@@ -1,12 +1,12 @@
+= render 'devise/shared/tab_single', tab_title: 'Resend unlock instructions'
 .login-box
-  .login-heading
-    %h3 Resend unlock email
   .login-body
-    = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f|
+    = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'show-gl-field-errors' }) do |f|
       .devise-errors
         = devise_error_messages!
-      .clearfix.append-bottom-20
-        = f.email_field :email, class: 'form-control', placeholder: 'Email', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off'
+      .form-group.append-bottom-20
+        = f.label :email
+        = f.email_field :email, class: 'form-control', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', title: 'Please provide a valid email address.'
       .clearfix
         = f.submit 'Resend unlock instructions', class: 'btn btn-success'
 
diff --git a/app/views/groups/group_members/_new_group_member.html.haml b/app/views/groups/group_members/_new_group_member.html.haml
index 2fb3190ab11e304162f349f77feb95c75caaafc7..b185b81db7ff5d2cee606034cdb82e0762520fd2 100644
--- a/app/views/groups/group_members/_new_group_member.html.haml
+++ b/app/views/groups/group_members/_new_group_member.html.haml
@@ -1,27 +1,22 @@
-= form_for @group_member, url: group_group_members_path(@group), html: { class: 'form-horizontal users-group-form' } do |f|
-  .form-group
-    = f.label :user_ids, "People", class: 'control-label'
-    .col-sm-10
-      = users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all, email_user: true)
-      .help-block
+= form_for @group_member, url: group_group_members_path(@group), html: { class: 'users-project-form users-group-form' } do |f|
+  .row
+    .col-md-4.col-lg-6
+      = users_select_tag(:user_ids, multiple: true, class: 'input-clamp', scope: :all, email_user: true)
+      .help-block.append-bottom-10
         Search for users by name, username, or email, or invite new ones using their email address.
 
-  .form-group
-    = f.label :access_level, "Group Access", class: 'control-label'
-    .col-sm-10
-      = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "project-access-select select2"
-      .help-block
-        Read more about role permissions
-        %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
+    .col-md-3.col-lg-2
+      = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "form-control project-access-select"
+      .help-block.append-bottom-10
+        = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
+        about role permissions
 
-  .form-group
-    = f.label :expires_at, 'Access expiration date', class: 'control-label'
-    .col-sm-10
+    .col-md-3.col-lg-2
       .clearable-input
-        = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
+        = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
         %i.clear-icon.js-clear-input
-      .help-block
+      .help-block.append-bottom-10
         On this date, the user(s) will automatically lose access to this group and all of its projects.
 
-  .form-actions
-    = f.submit 'Add users to group', class: "btn btn-create"
+    .col-md-2
+      = f.submit 'Add to group', class: "btn btn-create btn-block"
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index f789796e9429f006e248983bfcb12145f59f116f..ebf9aca7700849b90e958f0a851fa37c9f6fcafc 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -1,35 +1,31 @@
 - page_title "Members"
 
-.group-members-page.prepend-top-default
+.project-members-page.prepend-top-default
+  %h4
+    Members
+  %hr
   - if can?(current_user, :admin_group_member, @group)
-    .panel.panel-default
-      .panel-heading
-        Add new user to group
-      .panel-body
-        %p.light
-          Members of group have access to all group projects.
-        .new-group-member-holder
-          = render "new_group_member"
+    .project-members-new.append-bottom-default
+      %p.clearfix
+        Add new user to
+        %strong= @group.name
+      = render "new_group_member"
 
     = render 'shared/members/requests', membership_source: @group, requesters: @requesters
 
+  .append-bottom-default.clearfix
+    %h5.member.existing-title
+      Existing users
+    = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form'  do
+      .form-group
+        = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
+        %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
+          = icon("search")
   .panel.panel-default
     .panel-heading
+      Users with access to
       %strong #{@group.name}
-      group members
       %span.badge= @members.total_count
-      .controls
-        = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form'  do
-          .form-group
-            = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false }
-          = button_tag class: 'btn', title: 'Search' do
-            = icon("search")
     %ul.content-list
       = render partial: 'shared/members/member', collection: @members, as: :member
     = paginate @members, theme: 'gitlab'
-
-:javascript
-  $('form.member-search-form').on('submit', function(event) {
-    event.preventDefault();
-    Turbolinks.visit(this.action + '?' + $(this).serialize());
-  });
diff --git a/app/views/groups/group_members/update.js.haml b/app/views/groups/group_members/update.js.haml
index 3be7ed8432ce18d7a276dbf134a451a1b7d8867a..de8f53b6b52ba27088c628c6970303ace0a6fbc7 100644
--- a/app/views/groups/group_members/update.js.haml
+++ b/app/views/groups/group_members/update.js.haml
@@ -1,3 +1,3 @@
 :plain
-  $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}');
-  new gl.MemberExpirationDate();
+  var $listItem = $('#{escape_javascript(render('shared/members/member', member: @group_member))}');
+  $("##{dom_id(@group_member)} .list-item-name").replaceWith($listItem.find('.list-item-name'));
diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml
index 67ff4b272b9886a30016c469d71cb7ca760b7255..e138ebab0188767b60b2313c3883ea30ee26a4f2 100644
--- a/app/views/layouts/_init_auto_complete.html.haml
+++ b/app/views/layouts/_init_auto_complete.html.haml
@@ -1,7 +1,8 @@
 - project = @target_project || @project
 - noteable_type = @noteable.class if @noteable.present?
 
-:javascript
-  GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: noteable_type, type_id: params[:id])}"
-  GitLab.GfmAutoComplete.cachedData = undefined;
-  GitLab.GfmAutoComplete.setup();
+- if project
+  :javascript
+    GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: noteable_type, type_id: params[:id])}"
+    GitLab.GfmAutoComplete.cachedData = undefined;
+    GitLab.GfmAutoComplete.setup();
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 15a94ac23c56675e974b7d2b732a32a7a77554f6..6c2285fa2b6322fe1b5d241521aef3cd757deccb 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -11,3 +11,4 @@
     = render 'layouts/page', sidebar: sidebar, nav: nav
 
     = yield :scripts_body
+    = render "layouts/init_auto_complete" if @gfm_form
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index a9a384bd5f30386288b40ce300857262b47e1259..6922f1e153fb974ad29e5b4896b9ab50a1377c12 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -1,36 +1,37 @@
 !!! 5
-%html{ lang: "en"}
+%html{ lang: "en", class: "devise-layout-html"}
   = render "layouts/head"
-  %body.ui_charcoal.login-page.application.navless
-    = Gon::Base.render_data
-    = render "layouts/header/empty"
-    = render "layouts/broadcast"
-    .container.navless-container
-      .content
-        = render "layouts/flash"
-        .row
-          .col-sm-5.pull-right
-            = yield
-          .col-sm-7.brand-holder.pull-left
-            %h1
-              = brand_title
-            - if brand_item
-              = brand_image
-              = brand_text
-            - else
-              %h3 Open source software to collaborate on code
+  %body.ui_charcoal.login-page.application.navless{ data: { page: body_data_page }}
+    .page-wrap
+      = Gon::Base.render_data
+      = render "layouts/header/empty"
+      = render "layouts/broadcast"
+      .container.navless-container
+        .content
+          = render "layouts/flash"
+          .row
+            .col-sm-5.pull-right.new-session-forms-container
+              = yield
+            .col-sm-7.brand-holder.pull-left
+              %h1
+                = brand_title
+              - if brand_item
+                = brand_image
+                = brand_text
+              - else
+                %h3 Open source software to collaborate on code
 
-              %p
-                Manage git repositories with fine grained access controls that keep your code secure.
-                Perform code reviews and enhance collaboration with merge requests.
-                Each project can also have an issue tracker and a wiki.
+                %p
+                  Manage Git repositories with fine-grained access controls that keep your code secure.
+                  Perform code reviews and enhance collaboration with merge requests.
+                  Each project can also have an issue tracker and a wiki.
 
             - if current_application_settings.sign_in_text.present?
               = markdown_field(current_application_settings, :sign_in_text)
 
-    %hr
-    .container
-      .footer-links
-        = link_to "Explore", explore_root_path
-        = link_to "Help", help_path
-        = link_to "About GitLab", "https://about.gitlab.com/"
+      %hr.footer-fixed
+      .container.footer-container
+        .footer-links
+          = link_to "Explore", explore_root_path
+          = link_to "Help", help_path
+          = link_to "About GitLab", "https://about.gitlab.com/"
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index c80f22457b4eb8d673a7e39d46010bb86b7341f8..e2e974ba07219f16c8d37b1067e4b87a4a8842a8 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -86,11 +86,11 @@
           = f.label :username, "Path", class: "label-light"
           .input-group
             .input-group-addon
-              = "#{root_url}u/"
+              = root_url
             = f.text_field :username, required: true, class: 'form-control'
         .help-block
           Current path:
-          = "#{root_url}u/#{current_user.username}"
+          = "#{root_url}#{current_user.username}"
         .prepend-top-default
           = f.button class: "btn btn-warning", type: "submit" do
             = icon "spinner spin", class: "hidden loading-username"
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index cb97181b9e19ac61124f8074a8c9622315eab578..0c8241053e743d8e36247c937d06c6c6968587b3 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -1,3 +1,4 @@
+- @gfm_form = true
 - supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false)
 .zen-backdrop
   - classes << ' js-gfm-input js-autosize markdown-area'
@@ -7,6 +8,3 @@
     = text_area_tag attr, nil, class: classes, placeholder: placeholder
   %a.zen-control.zen-control-leave.js-zen-leave{ href: "#" }
     = icon('compress')
-
-- content_for :scripts_body do
-  = render "layouts/init_auto_complete" if current_user && (@target_project || @project)
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index 5a98e258b2204e73259ba72f9dc8d89d75b0fb98..dfb96305f482522052e0a50d37953ee16924946a 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,45 +1,48 @@
+- @no_container = true
 - page_title "Blame", @blob.path, @ref
+= render "projects/commits/head"
 
-%h3.page-title Blame view
+%div{ class: container_class }
+  %h3.page-title Blame view
 
-#blob-content-holder.tree-holder
-  .file-holder
-    .file-title
-      = blob_icon @blob.mode, @blob.name
-      %strong
-        = @path
-      %small= number_to_human_size @blob.size
-      .file-actions
-        = render "projects/blob/actions"
-    .table-responsive.file-content.blame.code.js-syntax-highlight
-      %table
-        - current_line = 1
-        - @blame_groups.each do |blame_group|
-          %tr
-            %td.blame-commit
-              .commit
-                - commit = blame_group[:commit]
-                = author_avatar(commit, size: 36)
-                .commit-row-title
-                  %strong
-                    = link_to_gfm truncate(commit.title, length: 35), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "cdark"
-                  .pull-right
-                    = link_to commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit), class: "monospace"
-                  &nbsp;
-                .light
-                  = commit_author_link(commit, avatar: false)
-                  authored
-                  #{time_ago_with_tooltip(commit.committed_date, skip_js: true)}
-            %td.line-numbers
-              - line_count = blame_group[:lines].count
-              - (current_line...(current_line + line_count)).each do |i|
-                %a.diff-line-num{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}
-                  = icon("link")
-                  = i
-                \
-              - current_line += line_count
-            %td.lines
-              %pre.code.highlight
-                %code
-                  - blame_group[:lines].each do |line|
-                    #{line}
+  #blob-content-holder.tree-holder
+    .file-holder
+      .file-title
+        = blob_icon @blob.mode, @blob.name
+        %strong
+          = @path
+        %small= number_to_human_size @blob.size
+        .file-actions
+          = render "projects/blob/actions"
+      .table-responsive.file-content.blame.code.js-syntax-highlight
+        %table
+          - current_line = 1
+          - @blame_groups.each do |blame_group|
+            %tr
+              %td.blame-commit
+                .commit
+                  - commit = blame_group[:commit]
+                  = author_avatar(commit, size: 36)
+                  .commit-row-title
+                    %strong
+                      = link_to_gfm truncate(commit.title, length: 35), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "cdark"
+                    .pull-right
+                      = link_to commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit), class: "monospace"
+                    &nbsp;
+                  .light
+                    = commit_author_link(commit, avatar: false)
+                    authored
+                    #{time_ago_with_tooltip(commit.committed_date, skip_js: true)}
+              %td.line-numbers
+                - line_count = blame_group[:lines].count
+                - (current_line...(current_line + line_count)).each do |i|
+                  %a.diff-line-num{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}
+                    = icon("link")
+                    = i
+                  \
+                - current_line += line_count
+              %td.lines
+                %pre.code.highlight
+                  %code
+                    - blame_group[:lines].each do |line|
+                      #{line}
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 680e95ac6b5bb7015f4bfe92986cdd9e2a3b7700..2a0352a71b79a19a3acfa49292061756b13e1db5 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -1,28 +1,31 @@
+- @no_container = true
 - page_title "Edit", @blob.path, @ref
 - content_for :page_specific_javascripts do
   = page_specific_javascript_tag('lib/ace.js')
   = page_specific_javascript_tag('blob_edit/blob_edit_bundle.js')
+= render "projects/commits/head"
 
-- if @conflict
-  .alert.alert-danger
-    Someone edited the file the same time you did. Please check out
-    = link_to "the file", namespace_project_blob_path(@project.namespace, @project, tree_join(@target_branch, @file_path)), target: "_blank"
-    and make sure your changes will not unintentionally remove theirs.
+%div{ class: container_class }
+  - if @conflict
+    .alert.alert-danger
+      Someone edited the file the same time you did. Please check out
+      = link_to "the file", namespace_project_blob_path(@project.namespace, @project, tree_join(@target_branch, @file_path)), target: "_blank"
+      and make sure your changes will not unintentionally remove theirs.
 
-.file-editor
-  %ul.nav-links.no-bottom.js-edit-mode
-    %li.active
-      = link_to '#editor' do
-        Edit File
+  .file-editor
+    %ul.nav-links.no-bottom.js-edit-mode
+      %li.active
+        = link_to '#editor' do
+          Edit File
 
-    %li
-      = link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do
-        = editing_preview_title(@blob.name)
+      %li
+        = link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do
+          = editing_preview_title(@blob.name)
 
-  = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths) do
-    = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
-    = render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
-    = hidden_field_tag 'last_commit_sha', @last_commit_sha
-    = hidden_field_tag 'content', '', id: "file-content"
-    = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
-    = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id)
+    = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-quick-submit js-requires-input js-edit-blob-form', data: blob_editor_paths) do
+      = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
+      = render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
+      = hidden_field_tag 'last_commit_sha', @last_commit_sha
+      = hidden_field_tag 'content', '', id: "file-content"
+      = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
+      = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id)
diff --git a/app/views/projects/builds/_table.html.haml b/app/views/projects/builds/_table.html.haml
index f3747ba2a21dfa94f521c2d030997271a0417c2f..36294c89fa807976f75cbe9f20b1b6e5749c9af5 100644
--- a/app/views/projects/builds/_table.html.haml
+++ b/app/views/projects/builds/_table.html.haml
@@ -5,7 +5,7 @@
     .nothing-here-block No builds to show
 - else
   .table-holder
-    %table.table.builds
+    %table.table.ci-table.builds-page
       %thead
         %tr
           %th Status
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index e4d41288aa6f34aef495e01eef019acc8e9c0cc2..b5e8b0bf6eb5e894c9b1681d6580b932a9a147dc 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -1,56 +1,59 @@
+- @no_container = true
 - page_title "#{@build.name} (##{@build.id})", "Builds"
 - trace_with_state = @build.trace_with_state
 - header_title project_title(@project, "Builds", project_builds_path(@project))
+= render "projects/pipelines/head", build_subnav: true
 
-.build-page
-  = render "header"
+%div{ class: container_class }
+  .build-page
+    = render "header"
 
-  - if @build.stuck?
-    - unless @build.any_runners_online?
-      .bs-callout.bs-callout-warning
-        %p
-          - if no_runners_for_project?(@build.project)
-            This build is stuck, because the project doesn't have any runners online assigned to it.
-          - elsif @build.tags.any?
-            This build is stuck, because you don't have any active runners online with any of these tags assigned to them:
-            - @build.tags.each do |tag|
-              %span.label.label-primary
-                = tag
-          - else
-            This build is stuck, because you don't have any active runners that can run this build.
+    - if @build.stuck?
+      - unless @build.any_runners_online?
+        .bs-callout.bs-callout-warning
+          %p
+            - if no_runners_for_project?(@build.project)
+              This build is stuck, because the project doesn't have any runners online assigned to it.
+            - elsif @build.tags.any?
+              This build is stuck, because you don't have any active runners online with any of these tags assigned to them:
+              - @build.tags.each do |tag|
+                %span.label.label-primary
+                  = tag
+            - else
+              This build is stuck, because you don't have any active runners that can run this build.
 
-          %br
-          Go to
-          = link_to namespace_project_runners_path(@build.project.namespace, @build.project) do
-            Runners page
+            %br
+            Go to
+            = link_to namespace_project_runners_path(@build.project.namespace, @build.project) do
+              Runners page
 
-  .prepend-top-default
-    - if @build.active?
-      .autoscroll-container
-        %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
-    - if @build.erased?
-      .erased.alert.alert-warning
-        - erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
-        Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
-    - else
-      #js-build-scroll.scroll-controls
-        = link_to '#build-trace', class: 'btn' do
-          %i.fa.fa-angle-up
-        = link_to '#down-build-trace', class: 'btn' do
-          %i.fa.fa-angle-down
-      %pre.build-trace#build-trace
-        %code.bash.js-build-output
-        = icon("refresh spin", class: "js-build-refresh")
+    .prepend-top-default
+      - if @build.active?
+        .autoscroll-container
+          %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
+      - if @build.erased?
+        .erased.alert.alert-warning
+          - erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
+          Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
+      - else
+        #js-build-scroll.scroll-controls
+          = link_to '#build-trace', class: 'btn' do
+            %i.fa.fa-angle-up
+          = link_to '#down-build-trace', class: 'btn' do
+            %i.fa.fa-angle-down
+        %pre.build-trace#build-trace
+          %code.bash.js-build-output
+          = icon("refresh spin", class: "js-build-refresh")
 
-    #down-build-trace
+      #down-build-trace
 
-= render "sidebar"
+  = render "sidebar"
 
-:javascript
-  new Build({
-    page_url: "#{namespace_project_build_url(@project.namespace, @project, @build)}",
-    build_url: "#{namespace_project_build_url(@project.namespace, @project, @build, :json)}",
-    build_status: "#{@build.status}",
-    build_stage: "#{@build.stage}",
-    state1: "#{trace_with_state[:state]}"
-  })
+  :javascript
+    new Build({
+      page_url: "#{namespace_project_build_url(@project.namespace, @project, @build)}",
+      build_url: "#{namespace_project_build_url(@project.namespace, @project, @build, :json)}",
+      build_status: "#{@build.status}",
+      build_stage: "#{@build.stage}",
+      state1: "#{trace_with_state[:state]}"
+    })
diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml
index 288c06d9b674aff958c95703f86ec82a0c23c781..d6916fb7f1ac2089d2aa886e4d244ea25c781f6c 100644
--- a/app/views/projects/commit/_pipeline.html.haml
+++ b/app/views/projects/commit/_pipeline.html.haml
@@ -56,7 +56,7 @@
     \.gitlab-ci.yml not found in this commit
 
 .table-holder.pipeline-holder
-  %table.table.builds.pipeline
+  %table.table.ci-table.pipeline
     %thead
       %tr
         %th Status
diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml
index 998812793a27d9e17dbc65c5865e63629b52e2f8..640651e93f512681400a9a877f6710b8ebb73253 100644
--- a/app/views/projects/commit/_pipelines_list.haml
+++ b/app/views/projects/commit/_pipelines_list.haml
@@ -4,7 +4,7 @@
       .nothing-here-block No pipelines to show
   - else
     .table-holder
-      %table.table.builds
+      %table.table.ci-table
         %tbody
           %th Status
           %th Pipeline
diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml
index 2f051fb90e076e3deac6de236963f0e502b4dcf0..f9d7eac354244867af3bce5c452a801532f2be37 100644
--- a/app/views/projects/commit/builds.html.haml
+++ b/app/views/projects/commit/builds.html.haml
@@ -1,7 +1,10 @@
+- @no_container = true
 - page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits"
+= render "projects/commits/head"
 
-.prepend-top-default
-  = render "commit_box"
+%div{ class: container_class }
+  .prepend-top-default
+    = render "commit_box"
 
-= render "ci_menu"
-= render "builds"
+  = render "ci_menu"
+  = render "builds"
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index ed44d86a687d0a1e36456d8b507650e12711c12b..cebf58d63dfe02bd61a3ad0ad3a6129dc3f38e27 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -1,14 +1,17 @@
+- @no_container = true
 - page_title        "#{@commit.title} (#{@commit.short_id})", "Commits"
 - page_description  @commit.description
+= render "projects/commits/head"
 
-.prepend-top-default
-  = render "commit_box"
-- if @commit.status
-  = render "ci_menu"
-- else
-  %div.block-connector
-= render "projects/diffs/diffs", diffs: @diffs
-= render "projects/notes/notes_with_form"
-- if can_collaborate_with_project?
-  - %w(revert cherry-pick).each do |type|
-    = render "projects/commit/change", type: type, commit: @commit, title: @commit.title
+%div{ class: container_class }
+  .prepend-top-default
+    = render "commit_box"
+  - if @commit.status
+    = render "ci_menu"
+  - else
+    %div.block-connector
+  = render "projects/diffs/diffs", diffs: @diffs
+  = render "projects/notes/notes_with_form"
+  - if can_collaborate_with_project?
+    - %w(revert cherry-pick).each do |type|
+      = render "projects/commit/change", type: type, commit: @commit, title: @commit.title
diff --git a/app/views/projects/environments/edit.html.haml b/app/views/projects/environments/edit.html.haml
index 6d1bdb9320f302ac72c4dd1c203eb2284f077a4b..3871165763c400de7bf4bb6f8f5b7ea394589829 100644
--- a/app/views/projects/environments/edit.html.haml
+++ b/app/views/projects/environments/edit.html.haml
@@ -1,6 +1,9 @@
+- @no_container = true
 - page_title "Edit", @environment.name, "Environments"
+= render "projects/pipelines/head"
 
-%h3.page-title
-  Edit environment
-%hr
-= render 'form'
+%div{ class: container_class }
+  %h3.page-title
+    Edit environment
+  %hr
+  = render 'form'
diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml
index ab80140972241f00a79d8725565df38d35a381d7..721ba156334e578aab01b47d022fe130ffece50a 100644
--- a/app/views/projects/environments/index.html.haml
+++ b/app/views/projects/environments/index.html.haml
@@ -24,7 +24,7 @@
             New environment
     - else
       .table-holder
-        %table.table.builds.environments
+        %table.table.ci-table.environments
           %tbody
             %th Environment
             %th Last Deployment
diff --git a/app/views/projects/environments/new.html.haml b/app/views/projects/environments/new.html.haml
index e51667ade2dbb1198fcc33e931c1ff287324f0ba..24638c77cbb0dfc864ac218de44940e8cd5ff06e 100644
--- a/app/views/projects/environments/new.html.haml
+++ b/app/views/projects/environments/new.html.haml
@@ -1,6 +1,9 @@
+- @no_container = true
 - page_title 'New Environment'
+= render "projects/pipelines/head"
 
-%h3.page-title
-  New environment
-%hr
-= render 'form'
+%div{ class: container_class }
+  %h3.page-title
+    New environment
+  %hr
+  = render 'form'
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index 7a8d196cf4e1c2d4f9a56f9579b94b084a43e737..90c59223a35728dcdf561ea05d3bf27016771339 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -24,7 +24,7 @@
         = link_to "Read more", help_page_path("ci/environments"), class: "btn btn-success"
     - else
       .table-holder
-        %table.table.builds.environments
+        %table.table.ci-table.environments
           %thead
             %tr
               %th ID
diff --git a/app/views/projects/group_links/update.js.haml b/app/views/projects/group_links/update.js.haml
new file mode 100644
index 0000000000000000000000000000000000000000..af9a5b190600c0bf5f4fb93c0260d4456003d5ea
--- /dev/null
+++ b/app/views/projects/group_links/update.js.haml
@@ -0,0 +1,3 @@
+:plain
+  var $listItem = $('#{escape_javascript(render('shared/members/group', group_link: @group_link))}');
+  $("#group_member_#{@group_link.id} .list-item-name").replaceWith($listItem.find('.list-item-name'));
diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml
index 6901ba13ab70e745531cb37c424a713769ef6921..52b187e7e58e866777a863da86894a3a6e0507be 100644
--- a/app/views/projects/labels/edit.html.haml
+++ b/app/views/projects/labels/edit.html.haml
@@ -1,6 +1,9 @@
+- @no_container = true
 - page_title "Edit", @label.name, "Labels"
+= render "projects/issues/head"
 
-%h3.page-title
-  Edit Label
-%hr
-= render 'form'
+%div{ class: container_class }
+  %h3.page-title
+    Edit Label
+  %hr
+  = render 'form'
diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml
index 49ddf9016192d95aa7d10e45090c89b2a41eb511..a1bb66cfb6c15e24444b7f754a9f17ed2f9b1c49 100644
--- a/app/views/projects/labels/new.html.haml
+++ b/app/views/projects/labels/new.html.haml
@@ -1,6 +1,9 @@
+- @no_container = true
 - page_title "New Label"
+= render "projects/issues/head"
 
-%h3.page-title
-  New Label
-%hr
-= render 'form'
+%div{ class: container_class }
+  %h3.page-title
+    New Label
+  %hr
+  = render 'form'
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 47dd51639b5ba265f427dfc19f67a19fe0906019..662463bc72bcbb388933b3e381a1c80b0f9b9c1e 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -47,7 +47,7 @@
           = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
 
     - if @commits_count.nonzero?
-      %ul.merge-request-tabs.nav-links.no-top.no-bottom
+      %ul.merge-request-tabs.nav-links.no-top.no-bottom{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
         %li.notes-tab
           = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
             Discussion
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index 5b7f83c344f5daebe01031c5dcb61385bc26b7a0..a82c846baa709dd737e91755a887b198af6c9c33 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -44,17 +44,5 @@
       = icon("times-circle")
       Could not connect to the CI server. Please check your settings and try again.
 
-- @merge_request.environments.sort_by(&:name).each do |environment|
-  - if can?(current_user, :read_environment, environment)
-    .mr-widget-heading
-      .ci_widget.ci-success
-        = ci_icon_for_status("success")
-        %span
-          Deployed to
-          = succeed '.' do
-            = link_to environment.name, environment_path(environment), class: 'environment'
-          - external_url = environment.external_url
-          - if external_url
-            = link_to external_url, target: '_blank' do
-              %span.hidden-xs View on #{external_url.gsub(/\A.*?:\/\//, '')}
-              = icon('external-link', right: true)
+.js-success-icon.hidden
+  = ci_icon_for_status('success')
diff --git a/app/views/projects/merge_requests/widget/_show.html.haml b/app/views/projects/merge_requests/widget/_show.html.haml
index ea618263a4a83dadc15ff23ce2ff5415623eaa22..608fdf1c5f5eb3d60c1a12291e6ca7f96e03b49e 100644
--- a/app/views/projects/merge_requests/widget/_show.html.haml
+++ b/app/views/projects/merge_requests/widget/_show.html.haml
@@ -12,6 +12,7 @@
     merge_check_url: "#{merge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
     check_enable: #{@merge_request.unchecked? ? "true" : "false"},
     ci_status_url: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
+    ci_environments_status_url: "#{ci_environments_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
     gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
     ci_status: "#{@merge_request.pipeline ? @merge_request.pipeline.status : ''}",
     ci_message: {
@@ -33,4 +34,4 @@
     merge_request_widget.clearEventListeners();
   }
 
-  merge_request_widget = new MergeRequestWidget(opts);
+  merge_request_widget = new window.gl.MergeRequestWidget(opts);
diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml
index be682226ab68e2b21436b8399d86cc8a88d2a78a..11f41e75e633750f444f99fd9ed3578ec71e89c5 100644
--- a/app/views/projects/milestones/edit.html.haml
+++ b/app/views/projects/milestones/edit.html.haml
@@ -1,8 +1,12 @@
+- @no_container = true
 - page_title "Edit", @milestone.title, "Milestones"
+= render "projects/issues/head"
 
-%h3.page-title
-  Edit Milestone ##{@milestone.iid}
+%div{ class: container_class }
 
-%hr
+  %h3.page-title
+    Edit Milestone ##{@milestone.iid}
 
-= render "form"
+  %hr
+
+  = render "form"
diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml
index 7f372b41698bd4f865e5bc2aeb9ea406528308a4..cda093ade819502f923f7bd2b8cd7e1994b95ba3 100644
--- a/app/views/projects/milestones/new.html.haml
+++ b/app/views/projects/milestones/new.html.haml
@@ -1,8 +1,11 @@
+- @no_container = true
 - page_title "New Milestone"
+= render "projects/issues/head"
 
-%h3.page-title
-  New Milestone
+%div{ class: container_class }
+  %h3.page-title
+    New Milestone
 
-%hr
+  %hr
 
-= render "form"
+  = render "form"
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index e62f810a52117f10ebb4568a839462228f956878..c83818e9199662cb71b2fb3bd859387171c7ca48 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,49 +1,52 @@
+- @no_container = true
 - page_title       @milestone.title, "Milestones"
 - page_description @milestone.description
+= render "projects/issues/head"
 
-.detail-page-header
-  .status-box{ class: status_box_class(@milestone) }
-    - if @milestone.closed?
-      Closed
-    - elsif @milestone.expired?
-      Past due
-    - else
-      Open
-  %span.identifier
-    Milestone ##{@milestone.iid}
-  - if @milestone.expires_at
-    %span.creator
-      &middot;
-      = @milestone.expires_at
-  .pull-right
-    - if can?(current_user, :admin_milestone, @project)
-      - if @milestone.active?
-        = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-nr btn-grouped"
+%div{ class: container_class }
+  .detail-page-header
+    .status-box{ class: status_box_class(@milestone) }
+      - if @milestone.closed?
+        Closed
+      - elsif @milestone.expired?
+        Past due
       - else
-        = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped"
+        Open
+    %span.identifier
+      Milestone ##{@milestone.iid}
+    - if @milestone.expires_at
+      %span.creator
+        &middot;
+        = @milestone.expires_at
+    .pull-right
+      - if can?(current_user, :admin_milestone, @project)
+        - if @milestone.active?
+          = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-nr btn-grouped"
+        - else
+          = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped"
 
-      = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped btn-nr" do
-        Edit
+        = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped btn-nr" do
+          Edit
 
-      = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do
-        Delete
+        = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do
+          Delete
 
-.detail-page-description.milestone-detail
-  %h2.title
-    = markdown_field(@milestone, :title)
-  %div
-    - if @milestone.description.present?
-      .description
-        .wiki
-          = preserve do
-            = markdown_field(@milestone, :description)
+  .detail-page-description.milestone-detail
+    %h2.title
+      = markdown_field(@milestone, :title)
+    %div
+      - if @milestone.description.present?
+        .description
+          .wiki
+            = preserve do
+              = markdown_field(@milestone, :description)
 
-- if @milestone.total_items_count(current_user).zero?
-  .alert.alert-success.prepend-top-default
-    %span Assign some issues to this milestone.
-- elsif @milestone.complete?(current_user) && @milestone.active?
-  .alert.alert-success.prepend-top-default
-    %span All issues for this milestone are closed. You may close this milestone now.
+  - if @milestone.total_items_count(current_user).zero?
+    .alert.alert-success.prepend-top-default
+      %span Assign some issues to this milestone.
+  - elsif @milestone.complete?(current_user) && @milestone.active?
+    .alert.alert-success.prepend-top-default
+      %span All issues for this milestone are closed. You may close this milestone now.
 
-= render 'shared/milestones/summary', milestone: @milestone, project: @project
-= render 'shared/milestones/tabs', milestone: @milestone
+  = render 'shared/milestones/summary', milestone: @milestone, project: @project
+  = render 'shared/milestones/tabs', milestone: @milestone
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 8352eba7446e5d16b0412581c4f720ffae1f1532..00b62a595ff213fe190071003c1868f6cb5fdf06 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -14,7 +14,7 @@
       .disabled-comment.text-center
         .disabled-comment-text.inline
           Please
-          = link_to "sign up", new_session_path(:user, redirect_to_referer: 'yes')
+          = link_to "register", new_session_path(:user, redirect_to_referer: 'yes')
           or
           = link_to "sign in", new_session_path(:user, redirect_to_referer: 'yes')
           to post a comment
diff --git a/app/views/projects/pipelines/_head.html.haml b/app/views/projects/pipelines/_head.html.haml
index 7d421c0e7409ef95535b663cb28ccef46b976dd1..b10dd47709f07e02eb6b6c61345511ac93e804b3 100644
--- a/app/views/projects/pipelines/_head.html.haml
+++ b/app/views/projects/pipelines/_head.html.haml
@@ -1,7 +1,7 @@
 = content_for :sub_nav do
   .scrolling-tabs-container.sub-nav-scroll
     = render 'shared/nav_scroll'
-    .nav-links.sub-nav.scrolling-tabs
+    .nav-links.sub-nav.scrolling-tabs{ class: ('build' if local_assigns.fetch(:build_subnav, false)) }
       %ul{ class: (container_class) }
         - if project_nav_tab? :pipelines
           = nav_link(controller: :pipelines) do
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index 2d1df095bfa061ce4629a96b8d9d9936a3a66550..9eeef5f57b49d812a55b23da5ce6f5f285d12fe8 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -43,7 +43,7 @@
         .nothing-here-block No pipelines to show
     - else
       .table-holder
-        %table.table.builds
+        %table.table.ci-table
           %thead
             %th.col-xs-1.col-sm-1 Status
             %th.col-xs-2.col-sm-4 Pipeline
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index 75943c64276bfd4b0318990caa700c0d46c2daeb..688535ad764f57b4d32171213b9df37229d9b83f 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -1,8 +1,11 @@
+- @no_container = true
 - page_title "Pipeline"
+= render "projects/pipelines/head"
 
-.prepend-top-default
-  - if @commit
-    = render "projects/pipelines/info"
-  %div.block-connector
+%div{ class: container_class }
+  .prepend-top-default
+    - if @commit
+      = render "projects/pipelines/info"
+    %div.block-connector
 
-= render "projects/commit/pipeline", pipeline: @pipeline
+  = render "projects/commit/pipeline", pipeline: @pipeline
diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml
index e783d8c72c52dd82190f1b21c48844555e7910fb..9738f369a35368d50cbb842893b7b4698066df3b 100644
--- a/app/views/projects/project_members/_group_members.html.haml
+++ b/app/views/projects/project_members/_group_members.html.haml
@@ -1,7 +1,7 @@
 .panel.panel-default
   .panel-heading
+    Group members with access to
     %strong #{@group.name}
-    group members
     %span.badge= members.size
     - if can?(current_user, :admin_group_member, @group)
       .controls
diff --git a/app/views/projects/project_members/_groups.html.haml b/app/views/projects/project_members/_groups.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..d7f5fa965270246888f60fa0ce1c5590e05df7ef
--- /dev/null
+++ b/app/views/projects/project_members/_groups.html.haml
@@ -0,0 +1,7 @@
+.panel.panel-default.project-members-groups
+  .panel-heading
+    Groups with access to
+    %strong #{@project.name}
+    %span.badge= group_links.size
+  %ul.content-list
+    = render partial: 'shared/members/group', collection: group_links, as: :group_link
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index fa8cbf717337b1c63a79f5ffc3ead340aff05e6c..79dcd7a6ee9acb501ef63897603233be50dcbd0d 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -1,27 +1,22 @@
-= form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'form-horizontal users-project-form' } do |f|
-  .form-group
-    = f.label :user_ids, "People", class: 'control-label'
-    .col-sm-10
-      = users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all, email_user: true)
-      .help-block
+= form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'users-project-form' } do |f|
+  .row
+    .col-md-4.col-lg-6
+      = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true)
+      .help-block.append-bottom-10
         Search for users by name, username, or email, or invite new ones using their email address.
 
-  .form-group
-    = f.label :access_level, "Project Access", class: 'control-label'
-    .col-sm-10
-      = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "project-access-select select2"
-      .help-block
-        Read more about role permissions
-        %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
+    .col-md-3.col-lg-2
+      = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select"
+      .help-block.append-bottom-10
+        = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
+        about role permissions
 
-  .form-group
-    = f.label :expires_at, 'Access expiration date', class: 'control-label'
-    .col-sm-10
+    .col-md-3.col-lg-2
       .clearable-input
-        = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
+        = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
         %i.clear-icon.js-clear-input
-      .help-block
+      .help-block.append-bottom-10
         On this date, the user(s) will automatically lose access to this project.
 
-  .form-actions
-    = f.submit 'Add users to project', class: "btn btn-create"
+    .col-md-2
+      = f.submit "Add to project", class: "btn btn-create btn-block"
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index b0bfdd235f7b24277909ee78f76705387fe8fe96..c1e894d8f40f1395c5c2dc46800634a21a4c44d9 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -1,19 +1,7 @@
 .panel.panel-default
   .panel-heading
+    Users with access to
     %strong #{@project.name}
-    project members
-    %span.badge= members.size
-    .controls
-      = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form'  do
-        .form-group
-          = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false }
-        = button_tag class: 'btn', title: 'Search' do
-          = icon("search")
+    %span.badge= @project_members.total_count
   %ul.content-list
     = render partial: 'shared/members/member', collection: members, as: :member
-
-:javascript
-  $('form.member-search-form').on('submit', function (event) {
-    event.preventDefault();
-    Turbolinks.visit(this.action + '?' + $(this).serialize());
-  });
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 9d063b3081f878a40c176954ac8ac18ce51cb892..bdeb704b6daa6a7c01393f5aab0f5778a168c185 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -1,24 +1,28 @@
 - page_title "Members"
 
-.project-members-page.js-project-members-page.prepend-top-default
+.project-members-page.prepend-top-default
+  %h4.project-members-title.clearfix
+    Members
+    = link_to "Import", import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-default pull-right hidden-xs", title: "Import members from another project"
   - if can?(current_user, :admin_project_member, @project)
-    .panel.panel-default
-      .panel-heading
-        Add new user to project
-        .controls
-          = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do
-            Import members
-      .panel-body
-        %p.light
-          Users with access to this project are listed below.
-        = render "new_project_member"
+    .project-members-new.append-bottom-default
+      %p.clearfix
+        Add new user to
+        %strong= @project.name
+      = render "new_project_member"
 
-    = render 'shared/members/requests', membership_source: @project, requesters: @requesters
+      = render 'shared/members/requests', membership_source: @project, requesters: @requesters
 
-  = render 'team', members: @project_members
-
-  - if @group
-    = render "group_members", members: @group_members
+  .append-bottom-default.clearfix
+    %h5.member.existing-title
+      Existing users and groups
+    = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form'  do
+      .form-group
+        = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
+        %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
+          = icon("search")
+  - if @group_links.any?
+    = render 'groups', group_links: @group_links
 
-  - if @project_group_links.any? && @project.allowed_to_share_with_group?
-    = render "shared_group_members"
+  = render 'team', members: @project_members
+  = paginate @project_members, theme: "gitlab"
diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml
index 37e55dc72a31b6457a87dbc207a0c02cd7e63974..91927181efbeba7fd4145f1a203189c5859e34a6 100644
--- a/app/views/projects/project_members/update.js.haml
+++ b/app/views/projects/project_members/update.js.haml
@@ -1,3 +1,3 @@
 :plain
-  $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @project_member))}');
-  new gl.MemberExpirationDate();
+  var $listItem = $('#{escape_javascript(render('shared/members/member', member: @project_member))}');
+  $("##{dom_id(@project_member)} .list-item-name").replaceWith($listItem.find('.list-item-name'));
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index c3f4e10c954e34007fed9dd8c933fa1a576ec8cb..a7944a6013083741c836f4fed01f1461e16c752a 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -23,6 +23,8 @@
           data: { data: issuable_template_names, field_name: 'issuable_template', selected: selected_template(issuable), project_path: ref_project.path, namespace_path: ref_project.namespace.path } } ) do
           %ul.dropdown-footer-list
             %li
+              %a.no-template
+                No template
               %a.reset-template
                 Reset template
   %div{ class: issuable_template_names.any? ? 'col-sm-7 col-lg-8' : 'col-sm-10' }
@@ -85,20 +87,20 @@
           .issuable-form-select-holder
             - if issuable.assignee_id
               = f.hidden_field :assignee_id
-            = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
+            = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
               placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee", show_menu_above: true } })
       .form-group.issue-milestone
         = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
         .col-sm-10{ class: ("col-lg-8" if has_due_date) }
           .issuable-form-select-holder
-            = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_menu_above: true, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input"
+            = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_menu_above: true, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
       .form-group
         - has_labels = issuable.project.labels.any?
         = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
         = f.hidden_field :label_ids, multiple: true, value: ''
         .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
           .issuable-form-select-holder
-            = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false, show_menu_above: 'true' }
+            = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false, show_menu_above: 'true' }, dropdown_title: "Select label"
     - if has_due_date
       .col-lg-6
         .form-group
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index 6d307611640a9f525b89fe77c85dc09c0eb9637c..22b5a6aa11bebb39a9f273de03656dc93564fea7 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -8,6 +8,7 @@
 - classes = local_assigns.fetch(:classes, [])
 - selected = local_assigns.fetch(:selected, nil)
 - selected_toggle = local_assigns.fetch(:selected_toggle, nil)
+- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by label")
 - dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path), labels: labels_filter_path, default_label: "Labels"}
 - dropdown_data.merge!(data_options)
 - classes << 'js-extra-options' if extra_options
@@ -23,7 +24,7 @@
       = multi_label_name(selected, "Labels")
     = icon('chevron-down')
   .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
-    = render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create }
+    = render partial: "shared/issuable/label_page_default", locals: { title: dropdown_title, show_footer: show_footer, show_create: show_create }
     - if show_create && project && can?(current_user, :admin_label, project)
       = render partial: "shared/issuable/label_page_create"
     = dropdown_loading
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
index ab3cc33d18f9c76fef5b7ecfcccc3eb1387d193f..f27a9002ec2d1f6e89132690395e223a5bdc53c8 100644
--- a/app/views/shared/issuable/_milestone_dropdown.html.haml
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -2,9 +2,10 @@
 - extra_class = extra_class || ''
 - show_menu_above = show_menu_above || false
 - selected_text = selected.try(:title)
+- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone")
 - if selected.present?
   = hidden_field_tag(name, name == :milestone_title ? selected.title : selected.id)
-= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: "Filter by milestone", toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
+= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
   placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected.try(:title), project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
   - if project
     %ul.dropdown-footer-list
diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..1c0346bbc78f9269a1625ca540237e7f0937f438
--- /dev/null
+++ b/app/views/shared/members/_group.html.haml
@@ -0,0 +1,29 @@
+- group_link = local_assigns[:group_link]
+- group = group_link.group
+- can_admin_member = can?(current_user, :admin_project_member, @project)
+%li.member.group_member{ id: "group_member_#{group_link.id}" }
+  %span{ class: "list-item-name" }
+    = image_tag group_icon(group), class: "avatar s40", alt: ''
+    %strong
+      = link_to group.name, group_path(group)
+    .cgray
+      Joined #{time_ago_with_tooltip(group.created_at)}
+      - if group_link.expires?
+        ·
+        %span{ class: ('text-warning' if group_link.expires_soon?) }
+          Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)}
+  .controls.member-controls
+    = form_tag namespace_project_group_link_path(@project.namespace, @project, group_link), method: :put, remote: true, class: 'form-horizontal js-edit-member-form' do
+      = select_tag 'group_link[group_access]', options_for_select(ProjectGroupLink.access_options, group_link.group_access), class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{group.id}", disabled: !can_admin_member
+      .prepend-left-5.clearable-input.member-form-control
+        = text_field_tag 'group_link[expires_at]', group_link.expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{group.id}", disabled: !can_admin_member
+        %i.clear-icon.js-clear-input
+    - if can_admin_member
+      = link_to namespace_project_group_link_path(@project.namespace, @project, group_link),
+        remote: true,
+        method: :delete,
+        data: { confirm: "Are you sure you want to remove #{group.name}?" },
+        class: 'btn btn-remove prepend-left-10' do
+        %span.visible-xs-block
+          Delete
+        = icon('trash', class: 'hidden-xs')
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index 5f20e4bd42af1dc32ec28128e48f311c250e97fc..432047a1c4ed6dd94fbfd947da4b3a2f79c7a0ba 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -1,59 +1,29 @@
 - show_roles = local_assigns.fetch(:show_roles, true)
 - show_controls = local_assigns.fetch(:show_controls, true)
-- user = member.user
+- user = local_assigns.fetch(:user, member.user)
+- source = member.source
+- can_admin_member = can?(current_user, action_member_permission(:update, member), member)
 
-%li.js-toggle-container{ class: dom_class(member), id: dom_id(member) }
-  - if show_roles
-    .controls
-      %strong.control-text= member.human_access
-      - if show_controls
-        - if !user && can?(current_user, action_member_permission(:admin, member), member.source)
-          = link_to 'Resend invite', polymorphic_path([:resend_invite, member]),
-                    method: :post,
-                    class: 'btn'
-
-        - if can?(current_user, action_member_permission(:update, member), member)
-          = button_tag icon('pencil'),
-                       type: 'button',
-                       class: 'btn inline js-toggle-button',
-                       title: 'Edit'
-
-          - if member.request?
-            = link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]),
-                      method: :post,
-                      class: 'btn btn-success',
-                      title: 'Grant access'
-
-        - if can?(current_user, action_member_permission(:destroy, member), member)
-          - if current_user == user
-            = link_to icon('sign-out', text: 'Leave'), polymorphic_path([:leave, member.source, :members]),
-                      method: :delete,
-                      data: { confirm: leave_confirmation_message(member.source) },
-                      class: 'btn btn-remove'
-          - else
-            = link_to icon('trash'), member,
-                      remote: true,
-                      method: :delete,
-                      data: { confirm: remove_member_message(member) },
-                      class: 'btn btn-remove',
-                      title: remove_member_title(member)
-
-
-  %span{ class: ("list-item-name" if show_controls) }
+%li.member{ class: dom_class(member), id: dom_id(member) }
+  %span.list-item-name
     - if user
       = image_tag avatar_icon(user, 40), class: "avatar s40", alt: ''
       %strong
         = link_to user.name, user_path(user)
-      %span.cgray= user.username
+      %span.cgray= user.to_reference
 
       - if user == current_user
-        %span.label.label-success It's you
+        %span.label.label-success.prepend-left-5 It's you
 
       - if user.blocked?
         %label.label.label-danger
           %strong Blocked
 
-      .cgray
+      - if source.instance_of?(Group) && !@group
+        = link_to source, class: "member-group-link prepend-left-5" do
+          = "· #{source.name}"
+
+      .hidden-xs.cgray
         - if member.request?
           Requested
           = time_ago_with_tooltip(member.requested_at)
@@ -73,20 +43,44 @@
           by
           = link_to member.created_by.name, user_path(member.created_by)
         = time_ago_with_tooltip(member.created_at)
-
   - if show_roles
-    .edit-member.hide.js-toggle-content
-      %br
-      = form_for member, remote: true, html: { class: 'form-horizontal' }  do |f|
-        .form-group
-          = label_tag "member_access_level_#{member.id}", 'Project access', class: 'control-label'
-          .col-sm-10
-            = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control', id: "member_access_level_#{member.id}"
-        .form-group
-          = label_tag "member_expires_at_#{member.id}", 'Access expiration date', class: 'control-label'
-          .col-sm-10
-            .clearable-input
-              = f.text_field :expires_at, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date', id: "member_expires_at_#{member.id}"
+    .controls.member-controls
+      - if show_controls
+        - if user != current_user
+          = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f|
+            = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member
+            .prepend-left-5.clearable-input.member-form-control
+              = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member
               %i.clear-icon.js-clear-input
-        .prepend-top-10
-          = f.submit 'Save', class: 'btn btn-save btn-sm'
+        - else
+          %span.member-access-text= member.human_access
+
+        - if member.invite? && can?(current_user, action_member_permission(:admin, member), member.source)
+          = link_to 'Resend invite', polymorphic_path([:resend_invite, member]),
+                    method: :post,
+                    class: 'btn btn-default  prepend-left-10'
+
+        - elsif member.request? && can_admin_member
+          = link_to icon('check inverse'), polymorphic_path([:approve_access_request, member]),
+                    method: :post,
+                    class: 'btn btn-success prepend-left-10',
+                    title: 'Grant access'
+
+        - if can?(current_user, action_member_permission(:destroy, member), member)
+          - if current_user == user
+            = link_to icon('sign-out', text: 'Leave'), polymorphic_path([:leave, member.source, :members]),
+                      method: :delete,
+                      data: { confirm: leave_confirmation_message(member.source) },
+                      class: 'btn btn-remove prepend-left-10'
+          - else
+            = link_to member,
+                      remote: true,
+                      method: :delete,
+                      data: { confirm: remove_member_message(member) },
+                      class: 'btn btn-remove prepend-left-10',
+                      title: remove_member_title(member) do
+              %span.visible-xs-block
+                Delete
+              = icon('trash', class: 'hidden-xs')
+      - else
+        %span.member-access-text= member.human_access
diff --git a/app/views/shared/members/_requests.html.haml b/app/views/shared/members/_requests.html.haml
index 40b39e850b00844cc1dd923452d6a6cde6567ef7..10050adfda5a4a59bbf8f369e4db0139da276300 100644
--- a/app/views/shared/members/_requests.html.haml
+++ b/app/views/shared/members/_requests.html.haml
@@ -1,8 +1,8 @@
 - if requesters.any?
   .panel.panel-default
     .panel-heading
+      Users requesting access to
       %strong= membership_source.name
-      access requests
       %span.badge= requesters.size
     %ul.content-list
       = render partial: 'shared/members/member', collection: requesters, as: :member
diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml
index 9657101ace5cba69f9bec900839cb121b3bf7bc8..232ca26c1af0a60faf0e2f99bf7d5a8300315736 100644
--- a/app/views/u2f/_authenticate.html.haml
+++ b/app/views/u2f/_authenticate.html.haml
@@ -6,7 +6,7 @@
 %script#js-authenticate-u2f-setup{ type: "text/template" }
   %div
     %p Insert your security key (if you haven't already), and press the button below.
-    %a.btn.btn-info#js-login-u2f-device{ href: 'javascript:void(0)' } Login Via U2F Device
+    %a.btn.btn-info#js-login-u2f-device{ href: 'javascript:void(0)' } Sign in via U2F device
 
 %script#js-authenticate-u2f-in-progress{ type: "text/template" }
   %p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
diff --git a/app/workers/build_coverage_worker.rb b/app/workers/build_coverage_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0680645a8dbda62773d87407281f134f5a6c97ff
--- /dev/null
+++ b/app/workers/build_coverage_worker.rb
@@ -0,0 +1,9 @@
+class BuildCoverageWorker
+  include Sidekiq::Worker
+  sidekiq_options queue: :default
+
+  def perform(build_id)
+    Ci::Build.find_by(id: build_id)
+      .try(:update_coverage)
+  end
+end
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e7286b77ac5caf4a510e5e28635698de95b7dc1c
--- /dev/null
+++ b/app/workers/build_finished_worker.rb
@@ -0,0 +1,10 @@
+class BuildFinishedWorker
+  include Sidekiq::Worker
+
+  def perform(build_id)
+    Ci::Build.find_by(id: build_id).try do |build|
+      BuildCoverageWorker.new.perform(build.id)
+      BuildHooksWorker.new.perform(build.id)
+    end
+  end
+end
diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e22ececb3fd4af4cf8b88b8a0ccb1801382566ee
--- /dev/null
+++ b/app/workers/build_hooks_worker.rb
@@ -0,0 +1,9 @@
+class BuildHooksWorker
+  include Sidekiq::Worker
+  sidekiq_options queue: :default
+
+  def perform(build_id)
+    Ci::Build.find_by(id: build_id)
+      .try(:execute_hooks)
+  end
+end
diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..500d357ce31c6e23c43b1e172d7a4376a0d89752
--- /dev/null
+++ b/app/workers/build_success_worker.rb
@@ -0,0 +1,27 @@
+class BuildSuccessWorker
+  include Sidekiq::Worker
+  sidekiq_options queue: :default
+
+  def perform(build_id)
+    Ci::Build.find_by(id: build_id).try do |build|
+      create_deployment(build)
+    end
+  end
+
+  private
+
+  def create_deployment(build)
+    return if build.environment.blank?
+
+    service = CreateDeploymentService.new(
+      build.project, build.user,
+      environment: build.environment,
+      sha: build.sha,
+      ref: build.ref,
+      tag: build.tag,
+      options: build.options.to_h[:environment],
+      variables: build.variables)
+
+    service.execute(build)
+  end
+end
diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ab5e9f6daad11a605a920f6a29c8723a57f5e489
--- /dev/null
+++ b/app/workers/pipeline_hooks_worker.rb
@@ -0,0 +1,9 @@
+class PipelineHooksWorker
+  include Sidekiq::Worker
+  sidekiq_options queue: :default
+
+  def perform(pipeline_id)
+    Ci::Pipeline.find_by(id: pipeline_id)
+      .try(:execute_hooks)
+  end
+end
diff --git a/app/workers/update_merge_requests_worker.rb b/app/workers/update_merge_requests_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..03f0528cdae6f180a446681580810681af770500
--- /dev/null
+++ b/app/workers/update_merge_requests_worker.rb
@@ -0,0 +1,16 @@
+class UpdateMergeRequestsWorker
+  include Sidekiq::Worker
+
+  def perform(project_id, user_id, oldrev, newrev, ref)
+    project = Project.find_by(id: project_id)
+    return unless project
+
+    user = User.find_by(id: user_id)
+    return unless user
+
+    MergeRequests::RefreshService.new(project, user).execute(oldrev, newrev, ref)
+
+    push_data = Gitlab::DataBuilder::Push.build(project, user, oldrev, newrev, ref, [])
+    SystemHooksService.new.execute_hooks(push_data, :push_hooks)
+  end
+end
diff --git a/config/routes.rb b/config/routes.rb
index 83c3a42c19f4e4ded4f2a7a8578d24812c46da3e..659ea51bc75b67e6dbcd842b53312483e1574112 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -88,4 +88,6 @@ Rails.application.routes.draw do
   get ':username.keys' => 'profiles/keys#get_keys', constraints: { username: /.*/ }
 
   root to: "root#index"
+
+  get '*unmatched_route', to: 'application#not_found'
 end
diff --git a/config/routes/group.rb b/config/routes/group.rb
index 47a8a0a53d4e261bf8af7b6285d79561849446e6..06b464d79c8760910581d0fce14a2639b48b5ba3 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -1,8 +1,14 @@
 require 'constraints/group_url_constrainer'
 
 constraints(GroupUrlConstrainer.new) do
-  scope(path: ':id', as: :group, controller: :groups) do
+  scope(path: ':id',
+        as: :group,
+        constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ },
+        controller: :groups) do
     get '/', action: :show
+    patch '/', action: :update
+    put '/', action: :update
+    delete '/', action: :destroy
   end
 end
 
diff --git a/config/routes/project.rb b/config/routes/project.rb
index f9d58f5d5b2d52a49f825c4a65c966061583708c..2cd8c60794a1a03c2ec2927f29ca1b4b51e305a9 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -273,6 +273,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
           post :merge
           post :cancel_merge_when_build_succeeds
           get :ci_status
+          get :ci_environments_status
           post :toggle_subscription
           post :remove_wip
           get :diff_for_path
@@ -407,7 +408,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
         end
       end
 
-      resources :group_links, only: [:index, :create, :destroy], constraints: { id: /\d+/ }
+      resources :group_links, only: [:index, :create, :update, :destroy], constraints: { id: /\d+/ }
 
       resources :notes, only: [:index, :create, :destroy, :update], concerns: :awardable, constraints: { id: /\d+/ } do
         member do
diff --git a/config/routes/user.rb b/config/routes/user.rb
index 54bbcb18f6a0d70c8c29e4b7dcb428ec0494ad34..dfb5d2a2ba4d4cae868493f92200c6bfed2d701a 100644
--- a/config/routes/user.rb
+++ b/config/routes/user.rb
@@ -33,5 +33,6 @@ scope(path: 'u/:username',
   get :projects
   get :contributed, as: :contributed_projects
   get :snippets
+  get :exists
   get '/', to: redirect('/%{username}')
 end
diff --git a/doc/README.md b/doc/README.md
index 7e3d9b00900d802b501e7ec1554b3df3962d4aed..c30bf32800337e80f8e25236bd4f74ef63e95dab 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -49,6 +49,7 @@
 - [Git LFS configuration](workflow/lfs/lfs_administration.md)
 - [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
 - [GitLab Performance Monitoring](administration/monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
+- [Request Profiling](administration/monitoring/performance/request_profiling.md) Get a detailed profile on slow requests.
 - [Monitoring uptime](user/admin_area/monitoring/health_check.md) Check the server status using the health check endpoint.
 - [Debugging Tips](administration/troubleshooting/debug.md) Tips to debug problems when things go wrong
 - [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs.
diff --git a/doc/administration/monitoring/performance/img/request_profile_result.png b/doc/administration/monitoring/performance/img/request_profile_result.png
new file mode 100644
index 0000000000000000000000000000000000000000..73e2fdcab679dc299607a24dcfdb97de8121dbaa
Binary files /dev/null and b/doc/administration/monitoring/performance/img/request_profile_result.png differ
diff --git a/doc/administration/monitoring/performance/img/request_profiling_token.png b/doc/administration/monitoring/performance/img/request_profiling_token.png
new file mode 100644
index 0000000000000000000000000000000000000000..04d87567816aaf0aa174c3506556111fc1f99e4f
Binary files /dev/null and b/doc/administration/monitoring/performance/img/request_profiling_token.png differ
diff --git a/doc/administration/monitoring/performance/request_profiling.md b/doc/administration/monitoring/performance/request_profiling.md
new file mode 100644
index 0000000000000000000000000000000000000000..c358dfbead24094cae7b50f3d4065eda2eef9e67
--- /dev/null
+++ b/doc/administration/monitoring/performance/request_profiling.md
@@ -0,0 +1,16 @@
+# Request Profiling
+
+## Procedure
+1. Grab the profiling token from `Monitoring > Requests Profiles` admin page
+(highlighted in a blue in the image below).
+![Profile token](img/request_profiling_token.png)
+1. Pass the header `X-Profile-Token: <token>` to the request you want to profile. You can use any of these tools
+    * [ModHeader](https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj) Chrome extension
+    * [Modify Headers](https://addons.mozilla.org/en-US/firefox/addon/modify-headers/) Firefox extension
+    * `curl --header 'X-Profile-Token: <token>' https://gitlab.example.com/group/project`
+1. Once request is finished (which will take a little longer than usual), you can
+view the profiling output from `Monitoring > Requests Profiles` admin page.
+![Profiling output](img/request_profile_result.png)
+
+## Cleaning up
+Profiling output will be cleared out every day via a Sidekiq worker.
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 59399861a974c4e00952cc2c2af6ec6d093506b0..84ea59ab6870b6ecc164d8ca62f043fe23ce3ff0 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -159,7 +159,8 @@ Variables can be also defined on [job level](#job-variables).
 > Introduced in GitLab Runner v0.7.0.
 
 `cache` is used to specify a list of files and directories which should be
-cached between builds.
+cached between builds. You can only use paths that are within the project
+workspace.
 
 **By default the caching is enabled per-job and per-branch.**
 
@@ -606,8 +607,8 @@ You can see a simple example at https://gitlab.com/gitlab-examples/review-apps-n
 > - Build artifacts are only collected for successful builds by default.
 
 `artifacts` is used to specify a list of files and directories which should be
-attached to the build after success. To pass artifacts between different builds,
-see [dependencies](#dependencies).
+attached to the build after success. You can only use paths that are within the
+project workspace. To pass artifacts between different builds, see [dependencies](#dependencies).
 
 Below are some examples.
 
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index 47ebcebc770ea8eaaf96432a9db5bc5ff77585a1..f07d2c9af2da7917ff056fd7b47e5731677ce5f7 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -472,4 +472,4 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain
 [doc-restart]: ../administration/restart_gitlab.md "GitLab restart documentation"
 [ce-3349]: https://gitlab.com/gitlab-org/gitlab-ce/issues/3349 "Documentation restructure"
 [graffle]: https://gitlab.com/gitlab-org/gitlab-design/blob/d8d39f4a87b90fb9ae89ca12dc565347b4900d5e/production/resources/gitlab-map.graffle
-[gitlab-map]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png
+[gitlab-map]: https://gitlab.com/gitlab-org/gitlab-design/raw/master/production/resources/gitlab-map.png
\ No newline at end of file
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 7ff603e2c4a91aff658cf9acf1c71c052eae692e..65d348290250cc1157312c7d5f63a249471a6f6c 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -34,10 +34,11 @@ graphs/dashboards.
 
 ## Tooling
 
-GitLab provides two built-in tools to aid the process of improving performance:
+GitLab provides built-in tools to aid the process of improving performance:
 
 * [Sherlock](profiling.md#sherlock)
 * [GitLab Performance Monitoring](../monitoring/performance/monitoring.md)
+* [Request Profiling](../administration/monitoring/performance/request_profiling.md)
 
 GitLab employees can use GitLab.com's performance monitoring systems located at
 <http://performance.gitlab.net>, this requires you to log in using your
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 1fa8678223a63213680a32644da1656a5b098a16..c9acc9cdfb090bb5dbd16617942d42eca4217ddc 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -400,7 +400,7 @@ If you are not using Linux you may have to run `gmake` instead of
     cd /home/git
     sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
     cd gitlab-workhorse
-    sudo -u git -H git checkout v0.8.4
+    sudo -u git -H git checkout v0.8.5
     sudo -u git -H make
 
 ### Initialize Database and Activate Advanced Features
diff --git a/doc/monitoring/performance/grafana_configuration.md b/doc/monitoring/performance/grafana_configuration.md
index 93320b40174b838582985f5639b696386caa5050..0d4be02ff5f03bac5d2a9dfbee5ab727c82881ef 100644
--- a/doc/monitoring/performance/grafana_configuration.md
+++ b/doc/monitoring/performance/grafana_configuration.md
@@ -1 +1 @@
-This document was moved to [administration/monitoring/performance/grafana_configuration](../administration/monitoring/performance/grafana_configuration.md).
+This document was moved to [administration/monitoring/performance/grafana_configuration](../../administration/monitoring/performance/grafana_configuration.md).
diff --git a/doc/university/README.md b/doc/university/README.md
index e71e49c33c8c280ba6bf94ea47040c89be318a22..f5a0dab39fe92dbba3aa48ab50ae332b5cd11f47 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -64,6 +64,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
 1. [Making GitLab Great for Everyone - Video](https://www.youtube.com/watch?v=GGC40y4vMx0) - Response to "Dear GitHub" letter
 1. [Using Innersourcing to Improve Collaboration](https://about.gitlab.com/2014/09/05/innersourcing-using-the-open-source-workflow-to-improve-collaboration-within-an-organization/)
 1. [The Software Development Market and GitLab - Video](https://www.youtube.com/watch?v=sXlhgPK1NTY&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e&index=6) - [Slides](https://docs.google.com/presentation/d/1vCU-NbZWz8NTNK8Vu3y4zGMAHb5DpC8PE5mHtw1PWfI/edit)
+1. [The GitLab Book Club](bookclub/index.md)
 
 #### 1.7 Community and Support
 
diff --git a/doc/university/bookclub/booklist.md b/doc/university/bookclub/booklist.md
new file mode 100644
index 0000000000000000000000000000000000000000..c4229832e9fcdc990d9f889884e0a413faad0b5b
--- /dev/null
+++ b/doc/university/bookclub/booklist.md
@@ -0,0 +1,113 @@
+# Books
+
+List of books and resources, that may be worth reading.
+
+## Papers
+
+1.  **The Humble Programmer**
+
+    Edsger W. Dijkstra, 1972 ([paper](http://dl.acm.org/citation.cfm?id=361591))
+
+## Programming
+
+1.  **Design Patterns: Elements of Reusable Object-Oriented Software**
+
+    Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, 1994 ([amazon](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612))
+
+1.  **Clean Code: A Handbook of Agile Software Craftsmanship**
+
+    Robert C. "Uncle Bob" Martin, 2008 ([amazon](http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882))
+
+1.  **Code Complete: A Practical Handbook of Software Construction**, 2nd Edition
+
+    Steve McConnell, 2004 ([amazon](http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670))
+
+1.  **The Pragmatic Programmer: From Journeyman to Master**
+
+    Andrew Hunt, David Thomas, 1999 ([amazon](http://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X))
+
+1.  **Working Effectively with Legacy Code**
+
+    Michael Feathers, 2004 ([amazon](http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052))
+
+1.  **Eloquent Ruby**
+
+    Russ Olsen, 2011 ([amazon](http://www.amazon.com/Eloquent-Ruby-Addison-Wesley-Professional/dp/0321584104))
+
+1.  **Domain-Driven Design: Tackling Complexity in the Heart of Software**
+
+    Eric Evans, 2003 ([amazon](http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215))
+
+1.  **How to Solve It: A New Aspect of Mathematical Method**
+
+    Polya G. 1957 ([amazon](http://www.amazon.com/How-Solve-Mathematical-Princeton-Science/dp/069116407X))
+
+1.  **Software Creativity 2.0**
+
+    Robert L. Glass, 2006 ([amazon](http://www.amazon.com/Software-Creativity-2-0-Robert-Glass/dp/0977213315))
+
+1.  **Object-Oriented Software Construction**
+
+    Bertrand Meyer, 1997 ([amazon](http://www.amazon.com/Object-Oriented-Software-Construction-Book-CD-ROM/dp/0136291554))
+
+1.  **Refactoring: Improving the Design of Existing Code**
+
+    Martin Fowler, Kent Beck, 1999 ([amazon](http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672))
+
+1.  **Test Driven Development: By Example**
+
+    Kent Beck, 2002 ([amazon](http://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530))
+
+1.  **Algorithms in C++: Fundamentals, Data Structure, Sorting, Searching**
+
+    Robert Sedgewick, 1990 ([amazon](http://www.amazon.com/Algorithms-Parts-1-4-Fundamentals-Structure/dp/0201350882))
+
+1.  **Effective C++**
+
+    Scott Mayers, 1996 ([amazon](http://www.amazon.com/Effective-Specific-Improve-Programs-Designs/dp/0321334876))
+
+1.  **Extreme Programming Explained: Embrace Change**
+
+    Kent Beck, 1999 ([amazon](http://www.amazon.com/Extreme-Programming-Explained-Embrace-Change/dp/0321278658))
+
+1.  **The Art of Computer Programming**
+
+    Donald E. Knuth, 1997 ([amazon](http://www.amazon.com/Computer-Programming-Volumes-1-4A-Boxed/dp/0321751043))
+
+1.  **Writing Efficient Programs**
+
+    Jon Louis Bentley, 1982 ([amazon](http://www.amazon.com/Writing-Efficient-Programs-Prentice-Hall-Software/dp/013970244X))
+
+1.  **The Mythical Man-Month: Essays on Software Engineering**
+
+    Frederick Phillips Brooks, 1975 ([amazon](http://www.amazon.com/Mythical-Man-Month-Essays-Software-Engineering/dp/0201006502))
+
+1.  **Peopleware: Productive Projects and Teams** 3rd Edition
+
+    Tom DeMarco, Tim Lister, 2013 ([amazon](http://www.amazon.com/Peopleware-Productive-Projects-Teams-3rd/dp/0321934113))
+
+1.  **Principles Of Software Engineering Management**
+
+    Tom Gilb, 1988 ([amazon](http://www.amazon.com/Principles-Software-Engineering-Management-Gilb/dp/0201192462))
+
+## Other
+
+1.  **Thinking, Fast and Slow**
+
+    Daniel Kahneman, 2013 ([amazon](http://www.amazon.com/Thinking-Fast-Slow-Daniel-Kahneman/dp/0374533555))
+
+1.  **The Social Animal** 11th Edition
+
+    Elliot Aronson, 2011 ([amazon](http://www.amazon.com/Social-Animal-Elliot-Aronson/dp/1429233419))
+
+1.  **Influence: Science and Practice** 5th Edition
+
+    Robert B. Cialdini, 2008 ([amazon](http://www.amazon.com/Influence-Practice-Robert-B-Cialdini/dp/0205609996))
+
+1.  **Getting to Yes: Negotiating Agreement Without Giving In**
+
+    Roger Fisher, William L. Ury, Bruce Patton, 2011 ([amazon](http://www.amazon.com/Getting-Yes-Negotiating-Agreement-Without/dp/0143118757))
+
+1.  **How to Win Friends & Influence People**
+
+    Dale Carnegie, 1981 ([amazon](http://www.amazon.com/How-Win-Friends-Influence-People/dp/0671027034))
diff --git a/doc/university/bookclub/index.md b/doc/university/bookclub/index.md
new file mode 100644
index 0000000000000000000000000000000000000000..022a61f44291dc68ed7292078f041c14a38d3cef
--- /dev/null
+++ b/doc/university/bookclub/index.md
@@ -0,0 +1,19 @@
+# The GitLab Book Club
+
+The Book Club is a casual meet-up to read and discuss books we like.
+We'll find a time that suits most, if not all.
+
+See the [book list](booklist.md) for additional recommendations.
+
+## Currently reading : Books about remote work
+
+1.  **Remote: Office not required**
+
+    David Heinemeier Hansson and Jason Fried, 2013
+    ([amazon](http://www.amazon.co.uk/Remote-Required-David-Heinemeier-Hansson/dp/0091954673))
+
+1.  **The Year Without Pants**
+
+    Scott Berkun, 2013 ([ScottBerkun.com](http://scottberkun.com/yearwithoutpants/))
+
+Any other books you'd like to suggest? Edit this page and add them to the queue.
diff --git a/doc/update/8.12-to-8.13.md b/doc/update/8.12-to-8.13.md
index 00d63c1b3c6edf43d592e56f5c44181ed2b770d1..8940d14559b880f98069250c8361aa1f337c0e05 100644
--- a/doc/update/8.12-to-8.13.md
+++ b/doc/update/8.12-to-8.13.md
@@ -84,7 +84,7 @@ GitLab 8.1.
 ```bash
 cd /home/git/gitlab-workhorse
 sudo -u git -H git fetch --all
-sudo -u git -H git checkout v0.8.4
+sudo -u git -H git checkout v0.8.5
 sudo -u git -H make
 ```
 
diff --git a/docker/README.md b/docker/README.md
index ee1f32adc26f9bf26719806598e84931dfa47efc..f9e12c5733b85098de0f41c5a5980ecdd18ac05a 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -2,6 +2,6 @@
 
 * The official GitLab Community Edition Docker image is [available on Docker Hub](https://hub.docker.com/r/gitlab/gitlab-ce/).
 * The official GitLab Enterprise Edition Docker image is [available on Docker Hub](https://hub.docker.com/r/gitlab/gitlab-ee/).
-* The complete usage guide can be found in [Using GitLab Docker images](http://doc.gitlab.com/omnibus/docker/)
+* The complete usage guide can be found in [Using GitLab Docker images](https://docs.gitlab.com/omnibus/docker/)
 * The Dockerfile used for building public images is in [Omnibus Repository](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master/docker)
-* Check the guide for [creating Omnibus-based Docker Image](http://doc.gitlab.com/omnibus/build/README.html#build-docker-image)
+* Check the guide for [creating Omnibus-based Docker Image](https://docs.gitlab.com/omnibus/build/README.html#build-docker-image)
diff --git a/features/groups.feature b/features/groups.feature
index 49e939807b5258addbd7494e4ff843794516af25..4044bd9be79c93a142b414283b1b465c4234cdbb 100644
--- a/features/groups.feature
+++ b/features/groups.feature
@@ -39,11 +39,6 @@ Feature: Groups
     When I visit group "Owned" merge requests page
     Then I should not see merge requests from the archived project
 
-  Scenario: I should see edit group "Owned" page
-    When I visit group "Owned" settings page
-    And I change group "Owned" name to "new-name"
-    Then I should see new group "Owned" name
-
   Scenario: I edit group "Owned" avatar
     When I visit group "Owned" settings page
     And I change group "Owned" avatar
diff --git a/features/steps/admin/groups.rb b/features/steps/admin/groups.rb
index 0c89a3db9ad1f0e09b5f44e285b135fba035824f..9396a76f0a288c7857433dc555f03b58562ac3cd 100644
--- a/features/steps/admin/groups.rb
+++ b/features/steps/admin/groups.rb
@@ -105,7 +105,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
       select "Developer", from: "access_level"
     end
 
-    click_button "Add users to group"
+    click_button "Add to group"
   end
 
   step 'I should see current user as "Developer"' do
diff --git a/features/steps/admin/projects.rb b/features/steps/admin/projects.rb
index d77945a6b9cd46b4ecb0a0bf33b5e52a021aaf37..2b8cd030acef899b1a5e4a2ad556c2516e5cd8cd 100644
--- a/features/steps/admin/projects.rb
+++ b/features/steps/admin/projects.rb
@@ -70,7 +70,7 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps
       select "Developer", from: "access_level"
     end
 
-    click_button "Add users to project"
+    click_button "Add to project"
   end
 
   step 'I should see current user as "Developer"' do
diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb
index e9b45823c67a72e95476541fe638bb7962b8a532..cefc55d07abef198a32cf024f6302b61557eb3f4 100644
--- a/features/steps/group/members.rb
+++ b/features/steps/group/members.rb
@@ -1,4 +1,5 @@
 class Spinach::Features::GroupMembers < Spinach::FeatureSteps
+  include WaitForAjax
   include SharedAuthentication
   include SharedPaths
   include SharedGroup
@@ -13,7 +14,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
       select "Reporter", from: "access_level"
     end
 
-    click_button "Add users to group"
+    click_button "Add to group"
   end
 
   step 'I select "Mike" as "Master"' do
@@ -24,7 +25,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
       select "Master", from: "access_level"
     end
 
-    click_button "Add users to group"
+    click_button "Add to group"
   end
 
   step 'I should see "Mike" in team list as "Reporter"' do
@@ -47,7 +48,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
       select "Reporter", from: "access_level"
     end
 
-    click_button "Add users to group"
+    click_button "Add to group"
   end
 
   step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
@@ -66,7 +67,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
       select "Reporter", from: "access_level"
     end
 
-    click_button "Add users to group"
+    click_button "Add to group"
   end
 
   step 'I should see user "John Doe" in team list' do
@@ -108,7 +109,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
   step 'I search for \'Mary\' member' do
     page.within '.member-search-form' do
       fill_in 'search', with: 'Mary'
-      click_button 'Search'
+      find('.member-search-btn').click
     end
   end
 
@@ -116,9 +117,8 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
     member = mary_jane_member
 
     page.within "#group_member_#{member.id}" do
-      click_button 'Edit'
       select 'Developer', from: "member_access_level_#{member.id}"
-      click_on 'Save'
+      wait_for_ajax
     end
   end
 
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 4fa7d7c656775b03cc8669ae6b6593a9bf723d26..0e81e99120bee607e5d0e97953ef6c26f150d3d1 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -73,18 +73,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
       author: current_user
   end
 
-  step 'I change group "Owned" name to "new-name"' do
-    fill_in 'group_name', with: 'new-name'
-    fill_in 'group_path', with: 'new-name'
-    click_button "Save group"
-  end
-
-  step 'I should see new group "Owned" name' do
-    page.within ".navbar-gitlab" do
-      expect(page).to have_content "new-name"
-    end
-  end
-
   step 'I change group "Owned" avatar' do
     attach_file(:group_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif'))
     click_button "Save group"
diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb
index b09ec86e5dfa7af77c1334f9b12777f92b0086ac..7490d2bc6e72ede7c6df518217530f9797acf5a4 100644
--- a/features/steps/project/graph.rb
+++ b/features/steps/project/graph.rb
@@ -19,8 +19,8 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
   end
 
   step 'page should have languages graphs' do
-    expect(page).to have_content "Ruby 66.63 %"
-    expect(page).to have_content "JavaScript 22.96 %"
+    expect(page).to have_content /Ruby 66.* %/
+    expect(page).to have_content /JavaScript 22.* %/
   end
 
   step 'page should have commits graphs' do
diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb
index e920f5a706ba44d60cfb5e78bcbab4f82cda9878..b21d0849ad1fc2a8954bafb18d6584850b607a72 100644
--- a/features/steps/project/team_management.rb
+++ b/features/steps/project/team_management.rb
@@ -22,7 +22,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
       select2(user.id, from: "#user_ids", multiple: true)
       select "Reporter", from: "access_level"
     end
-    click_button "Add users to project"
+    click_button "Add to project"
   end
 
   step 'I should see "Mike" in team list as "Reporter"' do
@@ -36,10 +36,10 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
 
   step 'I select "sjobs@apple.com" as "Reporter"' do
     page.within ".users-project-form" do
-      select2("sjobs@apple.com", from: "#user_ids", multiple: true)
+      find('#user_ids', visible: false).set('sjobs@apple.com')
       select "Reporter", from: "access_level"
     end
-    click_button "Add users to project"
+    click_button "Add to project"
   end
 
   step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
@@ -65,9 +65,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
     user = User.find_by(name: 'Dmitriy')
     project_member = project.project_members.find_by(user_id: user.id)
     page.within "#project_member_#{project_member.id}" do
-      click_button 'Edit'
       select "Reporter", from: "member_access_level_#{project_member.id}"
-      click_button "Save"
     end
   end
 
@@ -112,7 +110,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
   end
 
   step 'I click link "Import team from another project"' do
-    click_link "Import members from another project"
+    click_link "Import"
   end
 
   When 'I submit "Website" project for import team' do
@@ -144,8 +142,9 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
   end
 
   step 'I should see "Opensource" group user listing' do
-    expect(page).to have_content("Shared with OpenSource group, members with Master role (2)")
-    expect(page).to have_content(@os_user1.name)
-    expect(page).to have_content(@os_user2.name)
+    page.within '.project-members-groups' do
+      expect(page).to have_content('OpenSource')
+      expect(find('select').value).to eq('40')
+    end
   end
 end
diff --git a/features/support/db_cleaner.rb b/features/support/db_cleaner.rb
index 1ab308cfa556f4c640c5b4a51b00ae6d5a571aed..8294bb1445f104b2c537187a33aad7811f4002aa 100644
--- a/features/support/db_cleaner.rb
+++ b/features/support/db_cleaner.rb
@@ -1,6 +1,6 @@
 require 'database_cleaner'
 
-DatabaseCleaner.strategy = :truncation
+DatabaseCleaner[:active_record].strategy = :truncation
 
 Spinach.hooks.before_scenario do
   DatabaseCleaner.start
diff --git a/features/support/rerun.rb b/features/support/rerun.rb
index 8b176c5be895e397ddacb4c085488c9dee9ef5bc..60b78f9d05079d92df9d6e652f76349bc15b4f50 100644
--- a/features/support/rerun.rb
+++ b/features/support/rerun.rb
@@ -1,5 +1,7 @@
 # The spinach-rerun-reporter doesn't define the on_undefined_step
 # See it here: https://github.com/javierav/spinach-rerun-reporter/blob/master/lib/spinach/reporter/rerun.rb
+require 'spinach-rerun-reporter'
+
 module Spinach
   class Reporter
     class Rerun
diff --git a/lib/api/boards.rb b/lib/api/boards.rb
index 9b71d335128f63bb5ee484fbcf82568aa23ae4c5..b14dd4f6e83cf30055424f1e59dff1dec4597f57 100644
--- a/lib/api/boards.rb
+++ b/lib/api/boards.rb
@@ -3,19 +3,28 @@ module API
   class Boards < Grape::API
     before { authenticate! }
 
+    params do
+      requires :id, type: String, desc: 'The ID of a project'
+    end
     resource :projects do
-      # Get the project board
+      desc 'Get all project boards' do
+        detail 'This feature was introduced in 8.13'
+        success Entities::Board
+      end
       get ':id/boards' do
         authorize!(:read_board, user_project)
         present user_project.boards, with: Entities::Board
       end
 
+      params do
+        requires :board_id, type: Integer, desc: 'The ID of a board'
+      end
       segment ':id/boards/:board_id' do
         helpers do
           def project_board
             board = user_project.boards.first
 
-            if params[:board_id].to_i == board.id
+            if params[:board_id] == board.id
               board
             else
               not_found!('Board')
@@ -27,29 +36,35 @@ module API
           end
         end
 
-        # Get the lists of a project board
-        # Does not include `backlog` and `done` lists
+        desc 'Get the lists of a project board' do
+          detail 'Does not include `backlog` and `done` lists. This feature was introduced in 8.13'
+          success Entities::List
+        end
         get '/lists' do
           authorize!(:read_board, user_project)
           present board_lists, with: Entities::List
         end
 
-        # Get a list of a project board
+        desc 'Get a list of a project board' do
+          detail 'This feature was introduced in 8.13'
+          success Entities::List
+        end
+        params do
+          requires :list_id, type: Integer, desc: 'The ID of a list'
+        end
         get '/lists/:list_id' do
           authorize!(:read_board, user_project)
           present board_lists.find(params[:list_id]), with: Entities::List
         end
 
-        # Create a new board list
-        #
-        # Parameters:
-        #   id (required)           - The ID of a project
-        #   label_id (required)     - The ID of an existing label
-        # Example Request:
-        #   POST /projects/:id/boards/:board_id/lists
+        desc 'Create a new board list' do
+          detail 'This feature was introduced in 8.13'
+          success Entities::List
+        end
+        params do
+          requires :label_id, type: Integer, desc: 'The ID of an existing label'
+        end
         post '/lists' do
-          required_attributes! [:label_id]
-
           unless user_project.labels.exists?(params[:label_id])
             render_api_error!({ error: "Label not found!" }, 400)
           end
@@ -68,21 +83,21 @@ module API
           end
         end
 
-        # Moves a board list to a new position
-        #
-        # Parameters:
-        #   id (required) - The ID of a project
-        #   board_id (required) - The ID of a board
-        #   position (required) - The position of the list
-        # Example Request:
-        #   PUT /projects/:id/boards/:board_id/lists/:list_id
+        desc 'Moves a board list to a new position' do
+          detail 'This feature was introduced in 8.13'
+          success Entities::List
+        end
+        params do
+          requires :list_id,  type: Integer, desc: 'The ID of a list'
+          requires :position, type: Integer, desc: 'The position of the list'
+        end
         put '/lists/:list_id' do
           list = project_board.lists.movable.find(params[:list_id])
 
           authorize!(:admin_list, user_project)
 
           service = ::Boards::Lists::MoveService.new(user_project, current_user,
-              { position: params[:position].to_i })
+              { position: params[:position] })
 
           if service.execute(list)
             present list, with: Entities::List
@@ -91,14 +106,13 @@ module API
           end
         end
 
-        # Delete a board list
-        #
-        # Parameters:
-        #   id (required) - The ID of a project
-        #   board_id (required) - The ID of a board
-        #   list_id (required) - The ID of a board list
-        # Example Request:
-        #   DELETE /projects/:id/boards/:board_id/lists/:list_id
+        desc 'Delete a board list' do
+          detail 'This feature was introduced in 8.13'
+          success Entities::List
+        end
+        params do
+          requires :list_id, type: Integer, desc: 'The ID of a board list'
+        end
         delete "/lists/:list_id" do
           authorize!(:admin_list, user_project)
 
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index 19df13d8aacad3eca25872bda4e7ee8877d3de2e..832b04a3bb12f4375dd41e93e73ad1cfd4de5b67 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -8,18 +8,19 @@ module API
       'issues' => ->(id) { find_project_issue(id) }
     }
 
+    params do
+      requires :id, type: String, desc: 'The ID of a project'
+    end
     resource :projects do
       ISSUABLE_TYPES.each do |type, finder|
         type_id_str = "#{type.singularize}_id".to_sym
 
-        # Create a todo on an issuable
-        #
-        # Parameters:
-        #   id (required) - The ID of a project
-        #   issuable_id (required) - The ID of an issuable
-        # Example Request:
-        #   POST /projects/:id/issues/:issuable_id/todo
-        #   POST /projects/:id/merge_requests/:issuable_id/todo
+        desc 'Create a todo on an issuable' do
+          success Entities::Todo
+        end
+        params do
+          requires type_id_str, type: Integer, desc: 'The ID of an issuable'
+        end
         post ":id/#{type}/:#{type_id_str}/todo" do
           issuable = instance_exec(params[type_id_str], &finder)
           todo = TodoService.new.mark_todo(issuable, current_user).first
@@ -40,25 +41,21 @@ module API
         end
       end
 
-      # Get a todo list
-      #
-      # Example Request:
-      #  GET /todos
-      #
+      desc 'Get a todo list' do
+        success Entities::Todo
+      end
       get do
         todos = find_todos
 
         present paginate(todos), with: Entities::Todo, current_user: current_user
       end
 
-      # Mark a todo as done
-      #
-      # Parameters:
-      #   id: (required) - The ID of the todo being marked as done
-      #
-      # Example Request:
-      #  DELETE /todos/:id
-      #
+      desc 'Mark a todo as done' do
+        success Entities::Todo
+      end
+      params do
+        requires :id, type: Integer, desc: 'The ID of the todo being marked as done'
+      end
       delete ':id' do
         todo = current_user.todos.find(params[:id])
         TodoService.new.mark_todos_as_done([todo], current_user)
@@ -66,11 +63,7 @@ module API
         present todo.reload, with: Entities::Todo, current_user: current_user
       end
 
-      # Mark all todos as done
-      #
-      # Example Request:
-      #  DELETE /todos
-      #
+      desc 'Mark all todos as done'
       delete do
         todos = find_todos
         TodoService.new.mark_todos_as_done(todos, current_user)
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
index 6924a293da89b09f01f07279165fbf53aea98398..ce048a36fa063c602b63ef826cc4f5681a90c935 100644
--- a/lib/banzai/renderer.rb
+++ b/lib/banzai/renderer.rb
@@ -1,6 +1,6 @@
 module Banzai
   module Renderer
-    extend self
+    module_function
 
     # Convert a Markdown String into an HTML-safe String of HTML
     #
@@ -141,8 +141,6 @@ module Banzai
       end.html_safe
     end
 
-    private
-
     def cacheless_render(text, context = {})
       Gitlab::Metrics.measure(:banzai_cacheless_render) do
         result = render_result(text, context)
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index e4d996a3fb6de7050f0e53321435a0aa239a51d2..9b74364849e5e2c6251065930a9f145140dce548 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -113,17 +113,18 @@ module ExtractsPath
     @id = get_id
     @ref, @path = extract_ref(@id)
     @repo = @project.repository
-    if @options[:extended_sha1].blank?
-      @commit = @repo.commit(@ref)
-    else
-      @commit = @repo.commit(@options[:extended_sha1])
-    end
 
-    if @path.empty? && !@commit
-      @id = @ref = extract_ref_without_atom(@id)
+    if @options[:extended_sha1].present?
+      @commit = @repo.commit(@options[:extended_sha1])
+    else
       @commit = @repo.commit(@ref)
 
-      request.format = :atom if @commit
+      if @path.empty? && !@commit && @id.ends_with?('.atom')
+        @id = @ref = extract_ref_without_atom(@id)
+        @commit = @repo.commit(@ref)
+
+        request.format = :atom if @commit
+      end
     end
 
     raise InvalidPathError unless @commit
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index d0060fbaca1e8159bbeb335f51917e8fe8fa687a..9cec71a32220a2632ea7b0f47332ba9a92db2b51 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -47,8 +47,8 @@ module Gitlab
 
         unless File.size?(secret_file)
           # Generate a new token of 16 random hexadecimal characters and store it in secret_file.
-          token = SecureRandom.hex(16)
-          File.write(secret_file, token)
+          @secret_token = SecureRandom.hex(16)
+          File.write(secret_file, @secret_token)
         end
 
         link_path = File.join(shell_path, '.gitlab_shell_secret')
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 84298f8bef4b4a891406e814e668d42d87c5cfef..d509f0f2b9685078bcae3402e7eed73d7799c3f1 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -756,4 +756,34 @@ describe Projects::MergeRequestsController do
       post_assign_issues
     end
   end
+
+  describe 'GET ci_environments_status' do
+    context 'when the environment is from a forked project' do
+      let!(:forked)       { create(:project) }
+      let!(:environment)  { create(:environment, project: forked) }
+      let!(:deployment)   { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') }
+      let(:json_response) { JSON.parse(response.body) }
+      let(:admin)         { create(:admin) }
+
+      let(:merge_request) do
+        create(:forked_project_link, forked_to_project: forked,
+                                     forked_from_project: project)
+
+        create(:merge_request, source_project: forked, target_project: project)
+      end
+
+      before do
+        forked.team << [user, :master]
+
+        get :ci_environments_status,
+          namespace_id: merge_request.project.namespace.to_param,
+          project_id: merge_request.project.to_param,
+          id: merge_request.iid, format: 'json'
+      end
+
+      it 'links to the environment on that project' do
+        expect(json_response.first['url']).to match /#{forked.path_with_namespace}/
+      end
+    end
+  end
 end
diff --git a/spec/features/groups/members/owner_manages_access_requests_spec.rb b/spec/features/groups/members/owner_manages_access_requests_spec.rb
index 10d3713f19f7264582226365d3f98888340df6fa..d811b05b0c3023e4cbb39bf47fb590cfb6e27c55 100644
--- a/spec/features/groups/members/owner_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/owner_manages_access_requests_spec.rb
@@ -41,7 +41,7 @@ feature 'Groups > Members > Owner manages access requests', feature: true do
 
   def expect_visible_access_request(group, user)
     expect(group.requesters.exists?(user_id: user)).to be_truthy
-    expect(page).to have_content "#{group.name} access requests 1"
+    expect(page).to have_content "Users requesting access to #{group.name} 1"
     expect(page).to have_content user.name
   end
 end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index c54ec2563adb1ff12bd2d50a0dbc841f260a932b..13bfe90302cb0dffa0a5ca989867be56278ae73b 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -11,67 +11,99 @@ feature 'Group', feature: true do
     end
   end
 
-  describe 'creating a group with space in group path' do
-    it 'renders new group form with validation errors' do
-      visit new_group_path
-      fill_in 'Group path', with: 'space group'
+  describe 'create a group' do
+    before { visit new_group_path }
 
-      click_button 'Create group'
+    describe 'with space in group path' do
+      it 'renders new group form with validation errors' do
+        fill_in 'Group path', with: 'space group'
+        click_button 'Create group'
 
-      expect(current_path).to eq(groups_path)
-      expect(page).to have_namespace_error_message
+        expect(current_path).to eq(groups_path)
+        expect(page).to have_namespace_error_message
+      end
     end
-  end
-  
-  describe 'creating a group with .atom at end of group path' do
-    it 'renders new group form with validation errors' do
-      visit new_group_path
-      fill_in 'Group path', with: 'atom_group.atom'
 
-      click_button 'Create group'
+    describe 'with .atom at end of group path' do
+      it 'renders new group form with validation errors' do
+        fill_in 'Group path', with: 'atom_group.atom'
+        click_button 'Create group'
 
-      expect(current_path).to eq(groups_path)
-      expect(page).to have_namespace_error_message
+        expect(current_path).to eq(groups_path)
+        expect(page).to have_namespace_error_message
+      end
+    end
+
+    describe 'with .git at end of group path' do
+      it 'renders new group form with validation errors' do
+        fill_in 'Group path', with: 'git_group.git'
+        click_button 'Create group'
+
+        expect(current_path).to eq(groups_path)
+        expect(page).to have_namespace_error_message
+      end
     end
   end
-  
-  describe 'creating a group with .git at end of group path' do
-    it 'renders new group form with validation errors' do
-      visit new_group_path
-      fill_in 'Group path', with: 'git_group.git'
 
-      click_button 'Create group'
+  describe 'group edit' do
+    let(:group) { create(:group) }
+    let(:path)  { edit_group_path(group) }
+    let(:new_name) { 'new-name' }
+
+    before { visit path }
+
+    it 'saves new settings' do
+      fill_in 'group_name', with: new_name
+      click_button 'Save group'
+
+      expect(page).to have_content 'successfully updated'
+      expect(find('#group_name').value).to eq(new_name)
 
-      expect(current_path).to eq(groups_path)
-      expect(page).to have_namespace_error_message
+      page.within ".navbar-gitlab" do
+        expect(page).to have_content new_name
+      end
+    end
+
+    it 'removes group' do
+      click_link 'Remove Group'
+
+      expect(page).to have_content "scheduled for deletion"
     end
   end
 
-  describe 'description' do
+  describe 'group page with markdown description' do
     let(:group) { create(:group) }
     let(:path)  { group_path(group) }
 
     it 'parses Markdown' do
       group.update_attribute(:description, 'This is **my** group')
+
       visit path
+
       expect(page).to have_css('.description > p > strong')
     end
 
     it 'passes through html-pipeline' do
       group.update_attribute(:description, 'This group is the :poop:')
+
       visit path
+
       expect(page).to have_css('.description > p > img')
     end
 
     it 'sanitizes unwanted tags' do
       group.update_attribute(:description, '# Group Description')
+
       visit path
+
       expect(page).not_to have_css('.description h1')
     end
 
     it 'permits `rel` attribute on links' do
       group.update_attribute(:description, 'https://google.com/')
+
       visit path
+
       expect(page).to have_css('.description a[rel]')
     end
   end
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index 2523b4b78982c6d5afe78a1ca9d66a3ed8aae604..996f39ea06d1d8f646be90e72a9a2cd2114860ee 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -29,7 +29,7 @@ feature 'Login', feature: true do
 
   describe 'with two-factor authentication' do
     def enter_code(code)
-      fill_in 'Two-Factor Authentication code', with: code
+      fill_in 'user_otp_attempt', with: code
       click_button 'Verify code'
     end
 
diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb
index 4d5d4aa121add23e76759dc7de4fd0f318ac3001..a506624b30d71f4d4066f129a6d4ee024acd1c22 100644
--- a/spec/features/merge_requests/created_from_fork_spec.rb
+++ b/spec/features/merge_requests/created_from_fork_spec.rb
@@ -45,7 +45,7 @@ feature 'Merge request created from fork' do
       page.within('.merge-request-tabs') { click_link 'Builds' }
       wait_for_ajax
 
-      page.within('table.builds') do
+      page.within('table.ci-table') do
         expect(page).to have_content 'rspec'
         expect(page).to have_content 'spinach'
       end
diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
index cb3cea3fd51f0612456fe5a72770556b7d7daafb..7b8af555f0e3bf7ede508646cd90e9caef3bab6d 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
@@ -20,7 +20,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
       login_with(user)
       visit namespace_project_merge_request_path(project.namespace, project, merge_request)
     end
-    
+
     after do
       wait_for_ajax
     end
@@ -34,7 +34,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
           expect(page).to have_content 'Your commands have been executed!'
 
           expect(merge_request.reload.work_in_progress?).to eq true
-        end 
+        end
 
         it 'removes the WIP: prefix from the title' do
           merge_request.title = merge_request.wip_title
@@ -45,7 +45,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
           expect(page).to have_content 'Your commands have been executed!'
 
           expect(merge_request.reload.work_in_progress?).to eq false
-        end 
+        end
       end
 
       context 'when the current user cannot toggle the WIP prefix' do
diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8e23ec50d4af647c2abd2a7f99609fa063ef76ef
--- /dev/null
+++ b/spec/features/merge_requests/widget_deployments_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+feature 'Widget Deployments Header', feature: true, js: true do
+  include WaitForAjax
+
+  describe 'when deployed to an environment' do
+    let(:project)       { merge_request.target_project }
+    let(:merge_request) { create(:merge_request, :merged) }
+    let(:environment)   { create(:environment, project: project) }
+    let!(:deployment)   do
+      create(:deployment, environment: environment, sha: project.commit('master').id)
+    end
+
+    before do
+      login_as :admin
+      visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+    end
+
+    it 'displays that the environment is deployed' do
+      wait_for_ajax
+
+      expect(page).to have_content("Deployed to #{environment.name}")
+      expect(find('.ci_widget > span > span')['data-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
+    end
+  end
+end
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index cd79c4f512daf97ac46a43c42cd8b1aa0ea10225..d886909ce850fa3cc85ef8b130765cb7b31c1e3a 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -15,6 +15,7 @@ feature 'issuable templates', feature: true, js: true do
     let(:template_content) { 'this is a test "bug" template' }
     let(:longtemplate_content) { %Q(this\n\n\n\n\nis\n\n\n\n\na\n\n\n\n\nbug\n\n\n\n\ntemplate) }
     let(:issue) { create(:issue, author: user, assignee: user, project: project) }
+    let(:description_addition) { ' appending to description' }
 
     background do
       project.repository.commit_file(user, '.gitlab/issue_templates/bug.md', template_content, 'added issue template', 'master', false)
@@ -26,7 +27,26 @@ feature 'issuable templates', feature: true, js: true do
     scenario 'user selects "bug" template' do
       select_template 'bug'
       wait_for_ajax
-      preview_template(template_content)
+      preview_template
+      save_changes
+    end
+
+    scenario 'user selects "bug" template and then "no template"' do
+      select_template 'bug'
+      wait_for_ajax
+      select_option 'No template'
+      wait_for_ajax
+      preview_template('')
+      save_changes('')
+    end
+
+    scenario 'user selects "bug" template, edits description and then selects "reset template"' do
+      select_template 'bug'
+      wait_for_ajax
+      find_field('issue_description').send_keys(description_addition)
+      preview_template(template_content + description_addition)
+      select_option 'Reset template'
+      preview_template
       save_changes
     end
 
@@ -37,7 +57,7 @@ feature 'issuable templates', feature: true, js: true do
       wait_for_ajax
 
       end_height = page.evaluate_script('$(".markdown-area").outerHeight()')
-      
+
       expect(end_height).not_to eq(start_height)
     end
   end
@@ -75,7 +95,7 @@ feature 'issuable templates', feature: true, js: true do
     scenario 'user selects "feature-proposal" template' do
       select_template 'feature-proposal'
       wait_for_ajax
-      preview_template(template_content)
+      preview_template
       save_changes
     end
   end
@@ -102,25 +122,31 @@ feature 'issuable templates', feature: true, js: true do
         scenario 'user selects template' do
           select_template 'feature-proposal'
           wait_for_ajax
-          preview_template(template_content)
+          preview_template
           save_changes
         end
       end
     end
   end
 
-  def preview_template(expected_content)
+  def preview_template(expected_content = template_content)
     click_link 'Preview'
     expect(page).to have_content expected_content
+    click_link 'Write'
   end
 
-  def save_changes
+  def save_changes(expected_content = template_content)
     click_button "Save changes"
-    expect(page).to have_content template_content
+    expect(page).to have_content expected_content
   end
 
   def select_template(name)
     first('.js-issuable-selector').click
     first('.js-issuable-selector-wrap .dropdown-content a', text: name).click
   end
+
+  def select_option(name)
+    first('.js-issuable-selector').click
+    first('.js-issuable-selector-wrap .dropdown-footer-list a', text: name).click
+  end
 end
diff --git a/spec/features/projects/members/group_links_spec.rb b/spec/features/projects/members/group_links_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cc2f695211ce3e44e220f3351dd0c8fb2f08aeb8
--- /dev/null
+++ b/spec/features/projects/members/group_links_spec.rb
@@ -0,0 +1,66 @@
+require 'spec_helper'
+
+feature 'Projects > Members > Anonymous user sees members', feature: true, js: true do
+  include WaitForAjax
+
+  let(:user) { create(:user) }
+  let(:group) { create(:group, :public) }
+  let(:project) { create(:empty_project, :public) }
+
+  background do
+    project.team << [user, :master]
+    @group_link = create(:project_group_link, project: project, group: group)
+
+    login_as(user)
+    visit namespace_project_project_members_path(project.namespace, project)
+  end
+
+  it 'updates group access level' do
+    select 'Guest', from: "member_access_level_#{group.id}"
+    wait_for_ajax
+
+    visit namespace_project_project_members_path(project.namespace, project)
+
+    expect(page).to have_select("member_access_level_#{group.id}", selected: 'Guest')
+  end
+
+  it 'updates expiry date' do
+    tomorrow = Date.today + 3
+
+    fill_in "member_expires_at_#{group.id}", with: tomorrow.strftime("%F")
+    wait_for_ajax
+
+    page.within(find('li.group_member')) do
+      expect(page).to have_content('Expires in')
+    end
+  end
+
+  it 'deletes group link' do
+    page.within(first('.group_member')) do
+      find('.btn-remove').click
+    end
+    wait_for_ajax
+
+    expect(page).not_to have_selector('.group_member')
+  end
+
+  context 'search' do
+    it 'finds no results' do
+      page.within '.member-search-form' do
+        fill_in 'search', with: 'testing 123'
+        find('.member-search-btn').click
+      end
+
+      expect(page).not_to have_selector('.group_member')
+    end
+
+    it 'finds results' do
+      page.within '.member-search-form' do
+        fill_in 'search', with: group.name
+        find('.member-search-btn').click
+      end
+
+      expect(page).to have_selector('.group_member', count: 1)
+    end
+  end
+end
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index 430c384ac2ee824412a7051d55d80b927af3e6f5..27a83fdcd1f6d7235e5b82d52524e2af2ff3379e 100644
--- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
@@ -1,6 +1,7 @@
 require 'spec_helper'
 
 feature 'Projects > Members > Master adds member with expiration date', feature: true, js: true do
+  include WaitForAjax
   include Select2Helper
   include ActiveSupport::Testing::TimeHelpers
 
@@ -20,7 +21,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
       page.within '.users-project-form' do
         select2(new_member.id, from: '#user_ids', multiple: true)
         fill_in 'expires_at', with: '2016-08-10'
-        click_on 'Add users to project'
+        click_on 'Add to project'
       end
 
       page.within '.project_member:first-child' do
@@ -35,9 +36,8 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
       visit namespace_project_project_members_path(project.namespace, project)
 
       page.within '.project_member:first-child' do
-        click_on 'Edit'
-        fill_in 'Access expiration date', with: '2016-08-09'
-        click_on 'Save'
+        find('.js-access-expiration-date').set '2016-08-09'
+        wait_for_ajax
         expect(page).to have_content('Expires in 3 days')
       end
     end
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index f7fcd9b67313be821404ec7c6ff9088e50588dec..d15376931c388d5ecd4cd021a83c367d78919346 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -41,7 +41,7 @@ feature 'Projects > Members > Master manages access requests', feature: true do
 
   def expect_visible_access_request(project, user)
     expect(project.requesters.exists?(user_id: user)).to be_truthy
-    expect(page).to have_content "#{project.name} access requests 1"
+    expect(page).to have_content "Users requesting access to #{project.name} 1"
     expect(page).to have_content user.name
   end
 end
diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb
index a752c1d7235aff87995af8a82a9889bfe3cf9d59..65544f79eba817d192cbe90d3ee8b1caa89d8e6b 100644
--- a/spec/features/signup_spec.rb
+++ b/spec/features/signup_spec.rb
@@ -14,7 +14,7 @@ feature 'Signup', feature: true do
         fill_in 'new_user_username', with: user.username
         fill_in 'new_user_email',    with: user.email
         fill_in 'new_user_password', with: user.password
-        click_button "Sign up"
+        click_button "Register"
 
         expect(current_path).to eq users_almost_there_path
         expect(page).to have_content("Please check your email to confirm your account")
@@ -33,7 +33,7 @@ feature 'Signup', feature: true do
         fill_in 'new_user_username', with: user.username
         fill_in 'new_user_email',    with: user.email
         fill_in 'new_user_password', with: user.password
-        click_button "Sign up"
+        click_button "Register"
 
         expect(current_path).to eq dashboard_projects_path
         expect(page).to have_content("Welcome! You have signed up successfully.")
@@ -52,7 +52,7 @@ feature 'Signup', feature: true do
       fill_in 'new_user_username', with: user.username
       fill_in 'new_user_email',    with: existing_user.email
       fill_in 'new_user_password', with: user.password
-      click_button "Sign up"
+      click_button "Register"
 
       expect(current_path).to eq user_registration_path
       expect(page).to have_content("error prohibited this user from being saved")
@@ -69,7 +69,7 @@ feature 'Signup', feature: true do
       fill_in 'new_user_username', with: user.username
       fill_in 'new_user_email',    with: existing_user.email
       fill_in 'new_user_password', with: user.password
-      click_button "Sign up"
+      click_button "Register"
 
       expect(current_path).to eq user_registration_path
       expect(page.body).not_to match(/#{user.password}/)
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index ff6933dc8d90971ad8e6b27188ec434f1953409d..b750f27ea72d27b05aa082fe1e508e77c3794dd2 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -160,7 +160,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
         login_with(user)
 
         @u2f_device.respond_to_u2f_authentication
-        click_on "Login Via U2F Device"
+        click_on "Sign in via U2F device"
         expect(page.body).to match('We heard back from your U2F device')
         click_on "Authenticate via U2F Device"
 
@@ -174,7 +174,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
         login_with(user)
 
         @u2f_device.respond_to_u2f_authentication
-        click_on "Login Via U2F Device"
+        click_on "Sign in via U2F device"
         expect(page.body).to match('We heard back from your U2F device')
         click_on "Authenticate via U2F Device"
 
@@ -186,7 +186,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
       login_with(user, remember: true)
 
       @u2f_device.respond_to_u2f_authentication
-      click_on "Login Via U2F Device"
+      click_on "Sign in via U2F device"
       expect(page.body).to match('We heard back from your U2F device')
 
       within 'div#js-authenticate-u2f' do
@@ -209,7 +209,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
           # Try authenticating user with the old U2F device
           login_as(current_user)
           @u2f_device.respond_to_u2f_authentication
-          click_on "Login Via U2F Device"
+          click_on "Sign in via U2F device"
           expect(page.body).to match('We heard back from your U2F device')
           click_on "Authenticate via U2F Device"
 
@@ -230,7 +230,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
           # Try authenticating user with the same U2F device
           login_as(current_user)
           @u2f_device.respond_to_u2f_authentication
-          click_on "Login Via U2F Device"
+          click_on "Sign in via U2F device"
           expect(page.body).to match('We heard back from your U2F device')
           click_on "Authenticate via U2F Device"
 
@@ -244,7 +244,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
         unregistered_device = FakeU2fDevice.new(page, FFaker::Name.first_name)
         login_as(user)
         unregistered_device.respond_to_u2f_authentication
-        click_on "Login Via U2F Device"
+        click_on "Sign in via U2F device"
         expect(page.body).to match('We heard back from your U2F device')
         click_on "Authenticate via U2F Device"
 
@@ -271,7 +271,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
         [first_device, second_device].each do |device|
           login_as(user)
           device.respond_to_u2f_authentication
-          click_on "Login Via U2F Device"
+          click_on "Sign in via U2F device"
           expect(page.body).to match('We heard back from your U2F device')
           click_on "Authenticate via U2F Device"
 
diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb
index 6498b7317b487152b4d74889598ff0761de24f8a..ec4c4d62f535cef814da21e1c19cc6c98928379f 100644
--- a/spec/features/users_spec.rb
+++ b/spec/features/users_spec.rb
@@ -1,15 +1,16 @@
 require 'spec_helper'
 
-feature 'Users', feature: true do
+feature 'Users', feature: true, js: true do
   let(:user) { create(:user, username: 'user1', name: 'User 1', email: 'user1@gitlab.com') }
 
   scenario 'GET /users/sign_in creates a new user account' do
     visit new_user_session_path
+    click_link 'Register'
     fill_in 'new_user_name',     with: 'Name Surname'
     fill_in 'new_user_username', with: 'Great'
     fill_in 'new_user_email',    with: 'name@mail.com'
     fill_in 'new_user_password', with: 'password1234'
-    expect { click_button 'Sign up' }.to change { User.count }.by(1)
+    expect { click_button 'Register' }.to change { User.count }.by(1)
   end
 
   scenario 'Successful user signin invalidates password reset token' do
@@ -31,11 +32,12 @@ feature 'Users', feature: true do
 
   scenario 'Should show one error if email is already taken' do
     visit new_user_session_path
+    click_link 'Register'
     fill_in 'new_user_name',     with: 'Another user name'
     fill_in 'new_user_username', with: 'anotheruser'
     fill_in 'new_user_email',    with: user.email
     fill_in 'new_user_password', with: '12341234'
-    expect { click_button 'Sign up' }.to change { User.count }.by(0)
+    expect { click_button 'Register' }.to change { User.count }.by(0)
     expect(page).to have_text('Email has already been taken')
     expect(number_of_errors_on_page(page)).to be(1), 'errors on page:\n #{errors_on_page page}'
   end
@@ -51,6 +53,28 @@ feature 'Users', feature: true do
     end
   end
 
+  feature 'username validation' do
+    include WaitForAjax
+    let(:loading_icon) { '.fa.fa-spinner' }
+    let(:username_input) { 'new_user_username' }
+
+    before(:each) do
+      visit new_user_session_path
+      click_link 'Register'
+    end
+    scenario 'shows an error border if the username already exists' do
+      fill_in username_input, with: user.username
+      wait_for_ajax
+      expect(find('.username')).to have_css '.gl-field-error-outline'
+    end
+
+    scenario 'doesn\'t show an error border if the username is available' do
+      fill_in username_input, with: 'new-user'
+      wait_for_ajax
+      expect(find('#new_user_username')).not_to have_css '.gl-field-error-outline'
+    end
+  end
+
   def errors_on_page(page)
     page.find('#error_explanation').find('ul').all('li').map{ |item| item.text }.join("\n")
   end
diff --git a/spec/javascripts/fixtures/gl_field_errors.html.haml b/spec/javascripts/fixtures/gl_field_errors.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..2526e5e33a5671ce10839dee46f9e68fd58cd6e3
--- /dev/null
+++ b/spec/javascripts/fixtures/gl_field_errors.html.haml
@@ -0,0 +1,15 @@
+%form.show-gl-field-errors{action: 'submit', method: 'post'}
+  .form-group
+    %input.required-text{required: true, type: 'text'} Text
+  .form-group
+    %input.email{type: 'email', title: 'Please provide a valid email address.', required: true } Email
+  .form-group
+    %input.password{type: 'password', required: true} Password
+  .form-group
+    %input.alphanumeric{type: 'text', pattern: '[a-zA-Z0-9]', required: true} Alphanumeric
+  .form-group
+    %input.hidden{ type:'hidden' }
+  .form-group
+    %input.custom.no-gl-field-errors{ type:'text' } Custom, do not validate
+  .form-group
+  %input.submit{type: 'submit'} Submit
diff --git a/spec/javascripts/gl_field_errors_spec.js.es6 b/spec/javascripts/gl_field_errors_spec.js.es6
new file mode 100644
index 0000000000000000000000000000000000000000..36feb2b2aa59cd4e91f808d3073c0f6ca42102df
--- /dev/null
+++ b/spec/javascripts/gl_field_errors_spec.js.es6
@@ -0,0 +1,111 @@
+//= require jquery
+//= require gl_field_errors
+
+((global) => {
+  fixture.preload('gl_field_errors.html');
+
+  describe('GL Style Field Errors', function() {
+    beforeEach(function() {
+      fixture.load('gl_field_errors.html');
+      const $form = this.$form = $('form.show-gl-field-errors');
+      this.fieldErrors = new global.GlFieldErrors($form);
+    });
+
+    it('should properly initialize the form', function() {
+      expect(this.$form).toBeDefined();
+      expect(this.$form.length).toBe(1);
+      expect(this.fieldErrors).toBeDefined();
+      const inputs = this.fieldErrors.state.inputs;
+      expect(inputs.length).toBe(5);
+    });
+
+    it('should ignore elements with custom error handling', function() {
+      const customErrorFlag = 'no-gl-field-errors';
+      const customErrorElem = $(`.${customErrorFlag}`);
+
+      expect(customErrorElem.length).toBe(1);
+
+      const customErrors = this.fieldErrors.state.inputs.filter((input) => {
+       return input.inputElement.hasClass(customErrorFlag);
+      });
+      expect(customErrors.length).toBe(0);
+    });
+
+    it('should not show any errors before submit attempt', function() {
+      this.$form.find('.email').val('not-a-valid-email').keyup();
+      this.$form.find('.text-required').val('').keyup();
+      this.$form.find('.alphanumberic').val('?---*').keyup();
+
+      const errorsShown = this.$form.find('.gl-field-error-outline');
+      expect(errorsShown.length).toBe(0);
+    });
+
+    it('should show errors when input valid is submitted', function() {
+      this.$form.find('.email').val('not-a-valid-email').keyup();
+      this.$form.find('.text-required').val('').keyup();
+      this.$form.find('.alphanumberic').val('?---*').keyup();
+
+      this.$form.submit();
+
+      const errorsShown = this.$form.find('.gl-field-error-outline');
+      expect(errorsShown.length).toBe(4);
+    });
+
+    it('should properly track validity state on input after invalid submission attempt', function() {
+      this.$form.submit();
+
+      const emailInputModel = this.fieldErrors.state.inputs[1];
+      const fieldState = emailInputModel.state;
+      const emailInputElement = emailInputModel.inputElement;
+
+      // No input
+      expect(emailInputElement).toHaveClass('gl-field-error-outline');
+      expect(fieldState.empty).toBe(true);
+      expect(fieldState.valid).toBe(false);
+
+      // Then invalid input
+      emailInputElement.val('not-a-valid-email').keyup();
+      expect(emailInputElement).toHaveClass('gl-field-error-outline');
+      expect(fieldState.empty).toBe(false);
+      expect(fieldState.valid).toBe(false);
+
+      // Then valid input
+      emailInputElement.val('email@gitlab.com').keyup();
+      expect(emailInputElement).not.toHaveClass('gl-field-error-outline');
+      expect(fieldState.empty).toBe(false);
+      expect(fieldState.valid).toBe(true);
+
+      // Then invalid input
+      emailInputElement.val('not-a-valid-email').keyup();
+      expect(emailInputElement).toHaveClass('gl-field-error-outline');
+      expect(fieldState.empty).toBe(false);
+      expect(fieldState.valid).toBe(false);
+
+      // Then empty input
+      emailInputElement.val('').keyup();
+      expect(emailInputElement).toHaveClass('gl-field-error-outline');
+      expect(fieldState.empty).toBe(true);
+      expect(fieldState.valid).toBe(false);
+
+      // Then valid input
+      emailInputElement.val('email@gitlab.com').keyup();
+      expect(emailInputElement).not.toHaveClass('gl-field-error-outline');
+      expect(fieldState.empty).toBe(false);
+      expect(fieldState.valid).toBe(true);
+    });
+
+    it('should properly infer error messages', function() {
+      this.$form.submit();
+      const trackedInputs = this.fieldErrors.state.inputs;
+      const inputHasTitle = trackedInputs[1];
+      const hasTitleErrorElem = inputHasTitle.inputElement.siblings('.gl-field-error');
+      const inputNoTitle = trackedInputs[2];
+      const noTitleErrorElem = inputNoTitle.inputElement.siblings('.gl-field-error');
+
+      expect(noTitleErrorElem.text()).toBe('This field is required.');
+      expect(hasTitleErrorElem.text()).toBe('Please provide a valid email address.');
+    });
+
+  });
+
+})(window.gl || (window.gl = {}));
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 395032a74167d0415de157814e0234321c79cdf8..96ee5235acf4877db43056b3d75e9d9e0e66a4d4 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -1,5 +1,6 @@
 
 /*= require merge_request_tabs */
+//= require breakpoints
 
 (function() {
   describe('MergeRequestTabs', function() {
diff --git a/spec/javascripts/merge_request_widget_spec.js b/spec/javascripts/merge_request_widget_spec.js
index 17b32914ec395fe1150c493fa8d619903485fbcd..c9175e2b7046d7857a0daf02a6304eee96ead9d2 100644
--- a/spec/javascripts/merge_request_widget_spec.js
+++ b/spec/javascripts/merge_request_widget_spec.js
@@ -1,5 +1,5 @@
-
 /*= require merge_request_widget */
+/*= require lib/utils/jquery.timeago.js */
 
 (function() {
   describe('MergeRequestWidget', function() {
@@ -8,6 +8,7 @@
       window.notify = function() {};
       this.opts = {
         ci_status_url: "http://sampledomain.local/ci/getstatus",
+        ci_environments_status_url: "http://sampledomain.local/ci/getenvironmentsstatus",
         ci_status: "",
         ci_message: {
           normal: "Build {{status}} for \"{{title}}\"",
@@ -20,17 +21,48 @@
         gitlab_icon: "gitlab_logo.png",
         builds_path: "http://sampledomain.local/sampleBuildsPath"
       };
-      this["class"] = new MergeRequestWidget(this.opts);
-      return this.ciStatusData = {
-        "title": "Sample MR title",
-        "sha": "12a34bc5",
-        "status": "success",
-        "coverage": 98
-      };
+      this["class"] = new window.gl.MergeRequestWidget(this.opts);
     });
+
+    describe('getCIEnvironmentsStatus', function() {
+      beforeEach(function() {
+        this.ciEnvironmentsStatusData = [{
+          created_at: '2016-09-12T13:38:30.636Z',
+          environment_id: 1,
+          environment_name: 'env1',
+          external_url: 'https://test-url.com',
+          external_url_formatted: 'test-url.com'
+        }];
+
+        spyOn(jQuery, 'getJSON').and.callFake((req, cb) => {
+          cb(this.ciEnvironmentsStatusData);
+        });
+      });
+
+      it('should call renderEnvironments when the environments property is set', function() {
+         const spy = spyOn(this.class, 'renderEnvironments').and.stub();
+         this.class.getCIEnvironmentsStatus();
+         expect(spy).toHaveBeenCalledWith(this.ciEnvironmentsStatusData);
+       });
+
+       it('should not call renderEnvironments when the environments property is not set', function() {
+         this.ciEnvironmentsStatusData = null;
+         const spy = spyOn(this.class, 'renderEnvironments').and.stub();
+         this.class.getCIEnvironmentsStatus();
+         expect(spy).not.toHaveBeenCalled();
+       });
+    });
+
     return describe('getCIStatus', function() {
       beforeEach(function() {
-        return spyOn(jQuery, 'getJSON').and.callFake((function(_this) {
+        this.ciStatusData = {
+          "title": "Sample MR title",
+          "sha": "12a34bc5",
+          "status": "success",
+          "coverage": 98
+        };
+
+        spyOn(jQuery, 'getJSON').and.callFake((function(_this) {
           return function(req, cb) {
             return cb(_this.ciStatusData);
           };
@@ -61,10 +93,10 @@
         this["class"].getCIStatus(false);
         return expect(spy).not.toHaveBeenCalled();
       });
-      return it('should not display a notification on the first check after the widget has been created', function() {
+      it('should not display a notification on the first check after the widget has been created', function() {
         var spy;
         spy = spyOn(window, 'notify');
-        this["class"] = new MergeRequestWidget(this.opts);
+        this["class"] = new window.gl.MergeRequestWidget(this.opts);
         this["class"].getCIStatus(true);
         return expect(spy).not.toHaveBeenCalled();
       });
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index 7ce3884f8443f24d697bf47087d220e4e3253ee3..784b43d4846ec281809ccef47f8d72578516860a 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -21,7 +21,7 @@
       setupButton = this.container.find("#js-login-u2f-device");
       setupMessage = this.container.find("p");
       expect(setupMessage.text()).toContain('Insert your security key');
-      expect(setupButton.text()).toBe('Login Via U2F Device');
+      expect(setupButton.text()).toBe('Sign in via U2F device');
       setupButton.trigger('click');
       inProgressMessage = this.container.find("p");
       expect(inProgressMessage.text()).toContain("Trying to communicate with your device");
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 6b1867a44e1232805b92da166ed784d2479ba0ea..e172ee8e59075fba37a4e8330d68011fc928038d 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -64,6 +64,23 @@ describe Environment, models: true do
     end
   end
 
+  describe '#first_deployment_for' do
+    let(:project)       { create(:project) }
+    let!(:environment)  { create(:environment, project: project) }
+    let!(:deployment)   { create(:deployment, environment: environment, ref: commit.parent.id) }
+    let!(:deployment1)  { create(:deployment, environment: environment, ref: commit.id) }
+    let(:head_commit)   { project.commit }
+    let(:commit)        { project.commit.parent }
+
+    it 'returns deployment id for the environment' do
+      expect(environment.first_deployment_for(commit)).to eq deployment1
+    end
+
+    it 'return nil when no deployment is found' do
+      expect(environment.first_deployment_for(head_commit)).to eq nil
+    end
+  end
+
   describe '#environment_type' do
     subject { environment.environment_type }
 
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 308a00db9cdd8b1cb71b2bc54b617c29089ef661..67dbcc362f6115b780fa76e7aeab954462dbdea2 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -228,7 +228,6 @@ describe Project, models: true do
   describe 'Respond to' do
     it { is_expected.to respond_to(:url_to_repo) }
     it { is_expected.to respond_to(:repo_exists?) }
-    it { is_expected.to respond_to(:update_merge_requests) }
     it { is_expected.to respond_to(:execute_hooks) }
     it { is_expected.to respond_to(:owner) }
     it { is_expected.to respond_to(:path_with_namespace) }
@@ -389,26 +388,6 @@ describe Project, models: true do
     end
   end
 
-  describe '#update_merge_requests' do
-    let(:project) { create(:project) }
-    let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
-    let(:key) { create(:key, user_id: project.owner.id) }
-    let(:prev_commit_id) { merge_request.commits.last.id }
-    let(:commit_id) { merge_request.commits.first.id }
-
-    it 'closes merge request if last commit from source branch was pushed to target branch' do
-      project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.target_branch}", key.user)
-      merge_request.reload
-      expect(merge_request.merged?).to be_truthy
-    end
-
-    it 'updates merge request commits with new one if pushed to source branch' do
-      project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.source_branch}", key.user)
-      merge_request.reload
-      expect(merge_request.diff_head_sha).to eq(commit_id)
-    end
-  end
-
   describe '.find_with_namespace' do
     context 'with namespace' do
       before do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 4b80efbe12bd2b7f5a31fd8dcfc1c8714e1ce7b8..f977cf736733b676fcb86b10a3166d524ee02553 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -7,15 +7,18 @@ describe Repository, models: true do
   let(:project) { create(:project) }
   let(:repository) { project.repository }
   let(:user) { create(:user) }
+
   let(:commit_options) do
     author = repository.user_to_committer(user)
     { message: 'Test message', committer: author, author: author }
   end
+
   let(:merge_commit) do
     merge_request = create(:merge_request, source_branch: 'feature', target_branch: 'master', source_project: project)
     merge_commit_id = repository.merge(user, merge_request, commit_options)
     repository.commit(merge_commit_id)
   end
+
   let(:author_email) { FFaker::Internet.email }
 
   # I have to remove periods from the end of the name
@@ -90,6 +93,26 @@ describe Repository, models: true do
     end
   end
 
+  describe '#ref_name_for_sha' do
+    context 'ref found' do
+      it 'returns the ref' do
+        allow_any_instance_of(Gitlab::Popen).to receive(:popen).
+          and_return(["b8d95eb4969eefacb0a58f6a28f6803f8070e7b9 commit\trefs/environments/production/77\n", 0])
+
+        expect(repository.ref_name_for_sha('bla', '0' * 40)).to eq 'refs/environments/production/77'
+      end
+    end
+
+    context 'ref not found' do
+      it 'returns nil' do
+        allow_any_instance_of(Gitlab::Popen).to receive(:popen).
+          and_return(["", 0])
+
+        expect(repository.ref_name_for_sha('bla', '0' * 40)).to eq nil
+      end
+    end
+  end
+
   describe '#last_commit_for_path' do
     subject { repository.last_commit_for_path(sample_commit.id, '.gitignore').id }
 
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 5a1ed7d4a25f6b88f986554c399308b202a299d1..27f0fd22ae62a3f0716e28628149d83db5936be1 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -412,9 +412,10 @@ describe 'Git HTTP requests', lib: true do
 
         context "when the params are anything else" do
           let(:params) { { service: 'git-implode-pack' } }
+          before { get path, params }
 
-          it "fails to find a route" do
-            expect { get(path, params) }.to raise_error(ActionController::RoutingError)
+          it "redirects to the sign-in page" do
+            expect(response).to redirect_to(new_user_session_path)
           end
         end
       end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 0ee1c811dfb026c38f705d344b201dbc6dea0589..488dc1a63b0d0a1eb6bd5d0d3d5cbb91c7ebccfe 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -270,6 +270,12 @@ describe "Groups", "routing" do
 
     expect(get('/1')).to route_to('groups#show', id: '1')
   end
+
+  it "also display group#show with dot in the path" do
+    allow(Group).to receive(:find_by_path).and_return(true)
+
+    expect(get('/group.with.dot')).to route_to('groups#show', id: 'group.with.dot')
+  end
 end
 
 describe HealthCheckController, 'routing' do
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index 343b4385bf25254e936107c58ccc3be08c09a0ac..5fe56e7725f0af0f0824af202b9b5da388ad5605 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -84,11 +84,22 @@ describe CreateDeploymentService, services: true do
         expect(subject).to be_persisted
       end
     end
+
+    context 'when project was removed' do
+      let(:project) { nil }
+
+      it 'does not create deployment or environment' do
+        expect { subject }.not_to raise_error
+
+        expect(Environment.count).to be_zero
+        expect(Deployment.count).to be_zero
+      end
+    end
   end
 
   describe 'processing of builds' do
     let(:environment) { nil }
-    
+
     shared_examples 'does not create environment and deployment' do
       it 'does not create a new environment' do
         expect { subject }.not_to change { Environment.count }
@@ -133,12 +144,12 @@ describe CreateDeploymentService, services: true do
 
     context 'without environment specified' do
       let(:build) { create(:ci_build, project: project) }
-      
+
       it_behaves_like 'does not create environment and deployment' do
         subject { build.success }
       end
     end
-    
+
     context 'when environment is specified' do
       let(:pipeline) { create(:ci_pipeline, project: project) }
       let(:build) { create(:ci_build, pipeline: pipeline, environment: 'production', options: options) }
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index 8e3e12114f2f20e4943ba6d0546d6f6a99ea058a..dd2a9e9903a5563d21afad8c5a2ca9c531237bbd 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -184,8 +184,8 @@ describe GitPushService, services: true do
 
     context "Updates merge requests" do
       it "when pushing a new branch for the first time" do
-        expect(project).to receive(:update_merge_requests).
-                               with(@blankrev, 'newrev', 'refs/heads/master', user)
+        expect(UpdateMergeRequestsWorker).to receive(:perform_async).
+                                                with(project.id, user.id, @blankrev, 'newrev', 'refs/heads/master')
         execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' )
       end
     end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 5b4e4908add3a05bee329a76f838112a5d4c752c..e515bc9f89c2dabc066b3c9b776635a070026cca 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -62,7 +62,8 @@ describe MergeRequests::RefreshService, services: true do
 
       it { expect(@merge_request.notes).not_to be_empty }
       it { expect(@merge_request).to be_open }
-      it { expect(@merge_request.merge_when_build_succeeds).to be_falsey}
+      it { expect(@merge_request.merge_when_build_succeeds).to be_falsey }
+      it { expect(@merge_request.diff_head_sha).to eq(@newrev) }
       it { expect(@fork_merge_request).to be_open }
       it { expect(@fork_merge_request.notes).to be_empty }
       it { expect(@build_failed_todo).to be_done }
diff --git a/spec/views/projects/merge_requests/_heading.html.haml_spec.rb b/spec/views/projects/merge_requests/_heading.html.haml_spec.rb
deleted file mode 100644
index 86980f59cd8dc82dac5fbc4c14618905e8bf1065..0000000000000000000000000000000000000000
--- a/spec/views/projects/merge_requests/_heading.html.haml_spec.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require 'spec_helper'
-
-describe 'projects/merge_requests/widget/_heading' do
-  include Devise::Test::ControllerHelpers
-
-  context 'when released to an environment' do
-    let(:project)       { merge_request.target_project }
-    let(:merge_request) { create(:merge_request, :merged) }
-    let(:environment)   { create(:environment, project: project) }
-    let!(:deployment)   do
-      create(:deployment, environment: environment, sha: project.commit('master').id)
-    end
-
-    before do
-      assign(:merge_request, merge_request)
-      assign(:project, project)
-
-      allow(view).to receive(:can?).and_return(true)
-
-      render
-    end
-
-    it 'displays that the environment is deployed' do
-      expect(rendered).to match("Deployed to")
-      expect(rendered).to match("#{environment.name}")
-    end
-  end
-end
diff --git a/spec/workers/build_coverage_worker_spec.rb b/spec/workers/build_coverage_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ba20488f66343b2dfba8d18145986819bb6621ef
--- /dev/null
+++ b/spec/workers/build_coverage_worker_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe BuildCoverageWorker do
+  describe '#perform' do
+    context 'when build exists' do
+      let!(:build) { create(:ci_build) }
+
+      it 'updates code coverage' do
+        expect_any_instance_of(Ci::Build)
+          .to receive(:update_coverage)
+
+        described_class.new.perform(build.id)
+      end
+    end
+
+    context 'when build does not exist' do
+      it 'does not raise exception' do
+        expect { described_class.new.perform(123) }
+          .not_to raise_error
+      end
+    end
+  end
+end
diff --git a/spec/workers/build_finished_worker_spec.rb b/spec/workers/build_finished_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2868167c7d41917c39172a051f777c3076faaa63
--- /dev/null
+++ b/spec/workers/build_finished_worker_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+describe BuildFinishedWorker do
+  describe '#perform' do
+    context 'when build exists' do
+      let(:build) { create(:ci_build) }
+
+      it 'calculates coverage and calls hooks' do
+        expect(BuildCoverageWorker)
+          .to receive(:new).ordered.and_call_original
+        expect(BuildHooksWorker)
+          .to receive(:new).ordered.and_call_original
+
+        expect_any_instance_of(BuildCoverageWorker)
+          .to receive(:perform)
+        expect_any_instance_of(BuildHooksWorker)
+          .to receive(:perform)
+
+        described_class.new.perform(build.id)
+      end
+    end
+
+    context 'when build does not exist' do
+      it 'does not raise exception' do
+        expect { described_class.new.perform(123) }
+          .not_to raise_error
+      end
+    end
+  end
+end
diff --git a/spec/workers/build_hooks_worker_spec.rb b/spec/workers/build_hooks_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..97654a93f5c51f5b89708f0a4d3f0c849f16d102
--- /dev/null
+++ b/spec/workers/build_hooks_worker_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe BuildHooksWorker do
+  describe '#perform' do
+    context 'when build exists' do
+      let!(:build) { create(:ci_build) }
+
+      it 'calls build hooks' do
+        expect_any_instance_of(Ci::Build)
+          .to receive(:execute_hooks)
+
+        described_class.new.perform(build.id)
+      end
+    end
+
+    context 'when build does not exist' do
+      it 'does not raise exception' do
+        expect { described_class.new.perform(123) }
+          .not_to raise_error
+      end
+    end
+  end
+end
diff --git a/spec/workers/build_success_worker_spec.rb b/spec/workers/build_success_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dba7088313093ecd51217930c9c33ed86a5f3679
--- /dev/null
+++ b/spec/workers/build_success_worker_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe BuildSuccessWorker do
+  describe '#perform' do
+    context 'when build exists' do
+      context 'when build belogs to the environment' do
+        let!(:build) { create(:ci_build, environment: 'production') }
+
+        it 'executes deployment service' do
+          expect_any_instance_of(CreateDeploymentService)
+            .to receive(:execute)
+
+          described_class.new.perform(build.id)
+        end
+      end
+
+      context 'when build is not associated with project' do
+        let!(:build) { create(:ci_build, project: nil) }
+
+        it 'does not create deployment' do
+          expect_any_instance_of(CreateDeploymentService)
+            .not_to receive(:execute)
+
+          described_class.new.perform(build.id)
+        end
+      end
+    end
+
+    context 'when build does not exist' do
+      it 'does not raise exception' do
+        expect { described_class.new.perform(123) }
+          .not_to raise_error
+      end
+    end
+  end
+end
diff --git a/spec/workers/pipeline_hooks_worker_spec.rb b/spec/workers/pipeline_hooks_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..035e329839fd7a5f8f910b5118d9f88c895c45ad
--- /dev/null
+++ b/spec/workers/pipeline_hooks_worker_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe PipelineHooksWorker do
+  describe '#perform' do
+    context 'when pipeline exists' do
+      let(:pipeline) { create(:ci_pipeline) }
+
+      it 'executes hooks for the pipeline' do
+        expect_any_instance_of(Ci::Pipeline)
+          .to receive(:execute_hooks)
+
+        described_class.new.perform(pipeline.id)
+      end
+    end
+
+    context 'when pipeline does not exist' do
+      it 'does not raise exception' do
+        expect { described_class.new.perform(123) }
+          .not_to raise_error
+      end
+    end
+  end
+end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index ffeaafe654a893272ad174328cd6493e23124a99..984acdade3603c1964a5ed7b28c63267047d3ab7 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -92,7 +92,13 @@ describe PostReceive do
       allow(Project).to receive(:find_with_namespace).and_return(project)
       expect(project).to receive(:execute_hooks).twice
       expect(project).to receive(:execute_services).twice
-      expect(project).to receive(:update_merge_requests)
+
+      PostReceive.new.perform(pwd(project), key_id, base64_changes)
+    end
+
+    it "enqueues a UpdateMergeRequestsWorker job" do
+      allow(Project).to receive(:find_with_namespace).and_return(project)
+      expect(UpdateMergeRequestsWorker).to receive(:perform_async).with(project.id, project.owner.id, any_args)
 
       PostReceive.new.perform(pwd(project), key_id, base64_changes)
     end
diff --git a/spec/workers/update_merge_requests_worker_spec.rb b/spec/workers/update_merge_requests_worker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c78a69eda675a464dc71f9157da7855c54a19e47
--- /dev/null
+++ b/spec/workers/update_merge_requests_worker_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe UpdateMergeRequestsWorker do
+  include RepoHelpers
+
+  let(:project) { create(:project) }
+  let(:user) { create(:user) }
+
+  subject { described_class.new }
+
+  describe '#perform' do
+    let(:oldrev) { "123456" }
+    let(:newrev) { "789012" }
+    let(:ref)    { "refs/heads/test" }
+
+    def perform
+      subject.perform(project.id, user.id, oldrev, newrev, ref)
+    end
+
+    it 'executes MergeRequests::RefreshService with expected values' do
+      expect(MergeRequests::RefreshService).to receive(:new).with(project, user).and_call_original
+      expect_any_instance_of(MergeRequests::RefreshService).to receive(:execute).with(oldrev, newrev, ref)
+
+      perform
+    end
+
+    it 'executes SystemHooksService with expected values' do
+      push_data = double('push_data')
+      expect(Gitlab::DataBuilder::Push).to receive(:build).with(project, user, oldrev, newrev, ref, []).and_return(push_data)
+
+      system_hook_service = double('system_hook_service')
+      expect(SystemHooksService).to receive(:new).and_return(system_hook_service)
+      expect(system_hook_service).to receive(:execute_hooks).with(push_data, :push_hooks)
+
+      perform
+    end
+  end
+end