diff --git a/.eslintignore b/.eslintignore
index 453747e14e12ff6c0f61cfdbc973cf97b79b7dd4..d9c2233c9d784085598b00d48593355adf9e2b56 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,5 @@
+/coverage-javascript/
 /public/
 /tmp/
 /vendor/
-
+/builds/
diff --git a/.eslintrc b/.eslintrc
index 16eb18ecba2132ded93b682881b8e2c205656039..fd26215b84399973f3316d440a262b7406082c70 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,5 +1,11 @@
 {
   "extends": "airbnb",
+  "plugins": [
+    "filenames"
+  ],
+  "rules": {
+    "filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"]
+  },
   "globals": {
     "$": false,
     "_": false,
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 372ddecc98bac204e6bed68d2ead3b42d231d1c6..9411cc620031e39581eff8c77dd89328efb545a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,27 +1,45 @@
-Please view this file on the master branch, on stable branches it's out of date.
+**Note:** This file is automatically generated. Please see the [developer
+documentation](doc/development/changelog.md) for instructions on adding your own
+entry.
 
 ## 8.14.0 (2016-11-22)
 
+- Show correct environment log in admin/logs (@duk3luk3 !7191)
+- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
 - Backups do not fail anymore when using tar on annex and custom_hooks only. !5814
 - Adds user project membership expired event to clarify why user was removed (Callum Dryden)
 - Trim leading and trailing whitespace on project_path (Linus Thiel)
 - Prevent award emoji via notes for issues/MRs authored by user (barthc)
+- Adds support for the `token` attribute in project hooks API (Gauvain Pocentek)
 - Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO)
+- Fix Markdown styling inside reference links (Jan Zdráhal)
 - Fix extra space on Build sidebar on Firefox !7060
+- Fail gracefully when creating merge request with non-existing branch (alexsanford)
 - Fix mobile layout issues in admin user overview page !7087
 - Fix HipChat notifications rendering (airatshigapov, eisnerd)
+- Remove 'Edit' button from wiki edit view !7143 (Hiroyuki Sato)
+- Cleaned up global namespace JS !19661 (Jose Ivan Vargas)
 - Refactor Jira service to use jira-ruby gem
+- Improved todos empty state
 - Add hover to trash icon in notes !7008 (blackst0ne)
+- Hides project activity tabs when features are disabled
 - Only show one error message for an invalid email !5905 (lycoperdon)
+- Added guide describing how to upgrade PostgreSQL using Slony
 - Fix sidekiq stats in admin area (blackst0ne)
+- Added label description as tooltip to issue board list title
 - Created cycle analytics bundle JavaScript file
+- Hides container registry when repository is disabled
 - API: Fix booleans not recognized as such when using the `to_boolean` helper
 - Removed delete branch tooltip !6954
 - Stop unauthorized users dragging on milestone page (blackst0ne)
 - Restore issue boards welcome message when a project is created !6899
+- Check that JavaScript file names match convention !7238 (winniehell)
+- Do not show tooltip for active element !7105 (winniehell)
 - Escape ref and path for relative links !6050 (winniehell)
 - Fixed link typo on /help/ui to Alerts section. !6915 (Sam Rose)
+- Fix broken issue/merge request links in JIRA comments. !6143 (Brian Kintz)
 - Fix filtering of milestones with quotes in title (airatshigapov)
+- Fix issue boards dragging bug in Safari
 - Refactor less readable existance checking code from CoffeeScript !6289 (jlogandavison)
 - Update mail_room and enable sentinel support to Reply By Email (!7101)
 - Add task completion status in Issues and Merge Requests tabs: "X of Y tasks completed" (!6527, @gmesalazar)
@@ -30,16 +48,35 @@ Please view this file on the master branch, on stable branches it's out of date.
 - New issue board list dropdown stays open after adding a new list
 - Fix: Backup restore doesn't clear cache
 - Optimize Event queries by removing default order
+- Add new icon for skipped builds
+- Show created icon in pipeline mini-graph
+- Remove duplicate links from sidebar
 - API: Fix project deploy keys 400 and 500 errors when adding an existing key. !6784 (Joshua Welsh)
+- Add Rake task to create/repair GitLab Shell hooks symlinks !5634
 - Add job for removal of unreferenced LFS objects from both the database and the filesystem (Frank Groeneveld)
 - Replace jquery.cookie plugin with js.cookie !7085
 - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method
 - Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens
 - Show full status link on MR & commit pipelines
 - Fix documents and comments on Build API `scope`
+- Initialize Sidekiq with the list of queues used by GitLab
 - Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov)
 - Shortened merge request modal to let clipboard button not overlap
+- Adds JavaScript validation for group path editing field
 - In all filterable drop downs, put input field in focus only after load is complete (Ido @leibo)
+- Improve search query parameter naming in /admin/users !7115 (YarNayar)
+- Fix table pagination to be responsive
+- Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar)
+- Updated commit SHA styling on the branches page.
+
+## 8.13.3 (2016-11-02)
+
+- Removes any symlinks before importing a project export file. CVE-2016-9086
+- Fixed Import/Export foreign key issue to do with project members.
+- Fix relative links in Markdown wiki when displayed in "Project" tab !7218
+- Reduce the overhead to calculate number of open/closed issues and merge requests within the group or project
+- Fix project features default values
+- Changed build dropdown list length to be 6,5 builds long in the pipeline graph
 
 ## 8.13.2 (2016-10-31)
 
@@ -229,6 +266,11 @@ Please view this file on the master branch, on stable branches it's out of date.
 - Fix broken Project API docs (Takuya Noguchi)
 - Migrate invalid project members (owner -> master)
 
+## 8.12.8 (2016-11-02)
+
+- Removes any symlinks before importing a project export file. CVE-2016-9086
+- Fixed Import/Export foreign key issue to do with project members.
+
 ## 8.12.7
 
   - Prevent running `GfmAutocomplete` setup for each diff note. !6569
@@ -488,6 +530,10 @@ Please view this file on the master branch, on stable branches it's out of date.
   - Fix non-master branch readme display in tree view
   - Add UX improvements for merge request version diffs
 
+## 8.11.10 (2016-11-02)
+
+- Removes any symlinks before importing a project export file. CVE-2016-9086
+
 ## 8.11.9
 
   - Don't send Private-Token (API authentication) headers to Sentry
@@ -728,6 +774,10 @@ Please view this file on the master branch, on stable branches it's out of date.
   - Update gitlab_git gem to 10.4.7
   - Simplify SQL queries of marking a todo as done
 
+## 8.10.13 (2016-11-02)
+
+- Removes any symlinks before importing a project export file. CVE-2016-9086
+
 ## 8.10.12
 
   - Don't send Private-Token (API authentication) headers to Sentry
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b4635e50c2852d778a1deb52524c45392910acd5..67c30c2424c6360204ecd394168e38040562a08a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -19,7 +19,6 @@
         - [Technical debt](#technical-debt)
     - [Merge requests](#merge-requests)
         - [Merge request guidelines](#merge-request-guidelines)
-        - [Merge request description format](#merge-request-description-format)
         - [Contribution acceptance criteria](#contribution-acceptance-criteria)
     - [Changes for Stable Releases](#changes-for-stable-releases)
     - [Definition of done](#definition-of-done)
@@ -247,13 +246,7 @@ request is as follows:
 1. Fork the project into your personal space on GitLab.com
 1. Create a feature branch, branch away from `master`
 1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
-1. Add your changes to the [CHANGELOG.md](CHANGELOG.md):
-  1. If you are fixing a ~regression issue, you can add your entry to the next
-     patch release (e.g. `8.12.5` if current version is `8.12.4`)
-  1. Otherwise, add your entry to the next minor release (e.g. `8.13.0` if
-     current version is `8.12.4`
-  1. Please add your entry at a random place among the entries of the targeted
-     release
+1. [Generate a changelog entry with `bin/changelog`][changelog]
 1. If you are writing documentation, make sure to follow the
    [documentation styleguide][doc-styleguide]
 1. If you have multiple commits please combine them into one commit by
@@ -262,8 +255,11 @@ request is as follows:
 1. Submit a merge request (MR) to the `master` branch
 1. The MR title should describe the change you want to make
 1. The MR description should give a motive for your change and the method you
-   used to achieve it, see the [merge request description format]
-   (#merge-request-description-format)
+   used to achieve it.
+  1. If you are contributing code, fill in the template already provided in the
+     "Description" field.
+  1. If you are contributing documentation, choose `Documentation` from the
+     "Choose a template" menu and fill in the template.
 1. If the MR changes the UI it should include *Before* and *After* screenshots
 1. If the MR changes CSS classes please include the list of affected pages,
    `grep css-class ./app -R`
@@ -469,6 +465,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
 [contributor-covenant]: http://contributor-covenant.org
 [rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout
 [rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
+[changelog]: doc/development/changelog.md "Generate a changelog entry"
 [doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
 [scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
 [newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
diff --git a/Gemfile b/Gemfile
index 7e94c4051da2d451e2b8d28925cc23d3b4ad79dc..78748d0e9f870bb8dbc61fa7c0ddae36d92eeb4c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -104,7 +104,7 @@ gem 'deckar01-task_list', '1.0.5', require: 'task_list/railtie'
 gem 'gitlab-markup',      '~> 1.5.0'
 gem 'redcarpet',          '~> 3.3.3'
 gem 'RedCloth',           '~> 4.3.2'
-gem 'rdoc',               '~>3.6'
+gem 'rdoc',               '~> 4.2'
 gem 'org-ruby',           '~> 0.9.12'
 gem 'creole',             '~> 0.5.0'
 gem 'wikicloth',          '0.8.1'
@@ -117,7 +117,7 @@ gem 'truncato',           '~> 0.7.8'
 gem 'nokogiri', '~> 1.6.7', '>= 1.6.7.2'
 
 # Diffs
-gem 'diffy', '~> 3.0.3'
+gem 'diffy', '~> 3.1.0'
 
 # Application server
 group :unicorn do
@@ -196,7 +196,7 @@ gem 'loofah', '~> 2.0.3'
 gem 'licensee', '~> 8.0.0'
 
 # Protect against bruteforcing
-gem 'rack-attack', '~> 4.3.1'
+gem 'rack-attack', '~> 4.4.1'
 
 # Ace editor
 gem 'ace-rails-ap', '~> 4.1.0'
@@ -260,9 +260,6 @@ group :development do
   gem 'better_errors', '~> 1.0.1'
   gem 'binding_of_caller', '~> 0.7.2'
 
-  # Docs generator
-  gem 'sdoc', '~> 0.3.20'
-
   # thin instead webrick
   gem 'thin', '~> 1.7.0'
 end
diff --git a/Gemfile.lock b/Gemfile.lock
index 81af1ad2dac9bf7459aff48a6eeb46c6e88c0f07..3ecff5f6a6856e7de91dcad268fa42842d45fd29 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -180,7 +180,7 @@ GEM
       railties
       rotp (~> 2.0)
     diff-lcs (1.2.5)
-    diffy (3.0.7)
+    diffy (3.1.0)
     docile (1.1.5)
     doorkeeper (4.2.0)
       railties (>= 4.2)
@@ -520,7 +520,7 @@ GEM
     rack (1.6.4)
     rack-accept (0.4.5)
       rack (>= 0.4)
-    rack-attack (4.3.1)
+    rack-attack (4.4.1)
       rack
     rack-cors (0.4.0)
     rack-mount (0.8.3)
@@ -567,7 +567,7 @@ GEM
       ffi (>= 0.5.0)
     rblineprof (0.3.6)
       debugger-ruby_core_source (~> 1.3)
-    rdoc (3.12.2)
+    rdoc (4.2.2)
       json (~> 1.4)
     recaptcha (3.0.0)
       json
@@ -663,9 +663,6 @@ GEM
     scss_lint (0.47.1)
       rake (>= 0.9, < 11)
       sass (~> 3.4.15)
-    sdoc (0.3.20)
-      json (>= 1.1.3)
-      rdoc (~> 3.10)
     seed-fu (2.3.6)
       activerecord (>= 3.1)
       activesupport (>= 3.1)
@@ -847,7 +844,7 @@ DEPENDENCIES
   default_value_for (~> 3.0.0)
   devise (~> 4.2)
   devise-two-factor (~> 3.0.0)
-  diffy (~> 3.0.3)
+  diffy (~> 3.1.0)
   doorkeeper (~> 4.2.0)
   dropzonejs-rails (~> 0.7.1)
   email_reply_parser (~> 0.5.8)
@@ -929,14 +926,14 @@ DEPENDENCIES
   poltergeist (~> 1.9.0)
   premailer-rails (~> 1.9.0)
   pry-rails (~> 0.3.4)
-  rack-attack (~> 4.3.1)
+  rack-attack (~> 4.4.1)
   rack-cors (~> 0.4.0)
   rack-oauth2 (~> 1.2.1)
   rails (= 4.2.7.1)
   rails-deprecated_sanitizer (~> 1.0.3)
   rainbow (~> 2.1.0)
   rblineprof (~> 0.3.6)
-  rdoc (~> 3.6)
+  rdoc (~> 4.2)
   recaptcha (~> 3.0)
   redcarpet (~> 3.3.3)
   redis (~> 3.2)
@@ -956,7 +953,6 @@ DEPENDENCIES
   sanitize (~> 2.0)
   sass-rails (~> 5.0.6)
   scss_lint (~> 0.47.0)
-  sdoc (~> 0.3.20)
   seed-fu (~> 2.3.5)
   select2-rails (~> 3.5.9)
   sentry-raven (~> 2.0.0)
diff --git a/README.md b/README.md
index 46492f4b9c8e82bb9f9f9e5e6bfd6ac5222ae66a..dbe6db3ebed9a8ecd83f1656b18ea98dbe7cfe72 100644
--- a/README.md
+++ b/README.md
@@ -80,7 +80,7 @@ GitLab is a Ruby on Rails application that runs on the following software:
 - Redis 2.8+
 - MySQL or PostgreSQL
 
-For more information please see the [architecture documentation](http://doc.gitlab.com/ce/development/architecture.html).
+For more information please see the [architecture documentation](https://docs.gitlab.com/ce/development/architecture.html).
 
 ## Third-party applications
 
@@ -96,7 +96,7 @@ For upgrading information please see our [update page](https://about.gitlab.com/
 
 ## Documentation
 
-All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/).
+All documentation can be found on [docs.gitlab.com/ce/](https://docs.gitlab.com/ce/).
 
 ## Getting help
 
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 7ebe1599fca22a548f61946098380acb700b73fd..1cab66e109e57d7b44b15a797c4f77b23994920c 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -22,16 +22,14 @@
       });
     },
     // Return groups list. Filtered by query
-    // Only active groups retrieved
-    groups: function(query, skip_ldap, skip_groups, callback) {
+    groups: function(query, options, callback) {
       var url = Api.buildUrl(Api.groupsPath);
       return $.ajax({
         url: url,
-        data: {
-          search: query,
-          skip_groups: skip_groups,
-          per_page: 20
-        },
+        data: $.extend({
+                search: query,
+                per_page: 20
+              }, options),
         dataType: "json"
       }).done(function(groups) {
         return callback(groups);
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index e57cf1b3a5841bef75e11cc8c689069ba3f0a500..7d942de01846b7fbf86890a5cb5cd36ec44a3881 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -1,6 +1,6 @@
 /* eslint-disable */
 // This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// Add new JavaScript code in separate files in this directory and they'll automatically
 // be included in the compiled file accessible from http://example.com/assets/application.js
 // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
 // the compiled file.
@@ -13,6 +13,7 @@
 /*= require jquery-ui/sortable */
 /*= require jquery_ujs */
 /*= require jquery.endless-scroll */
+/*= require jquery.timeago */
 /*= require jquery.highlight */
 /*= require jquery.waitforimages */
 /*= require jquery.atwho */
@@ -54,125 +55,53 @@
 /*= require_directory . */
 /*= require fuzzaldrin-plus */
 
-(function() {
-  window.slugify = function(text) {
-    return text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase();
-  };
-
-  window.ajaxGet = function(url) {
-    return $.ajax({
-      type: "GET",
-      url: url,
-      dataType: "script"
-    });
-  };
-
-  window.split = function(val) {
-    return val.split(/,\s*/);
-  };
-
-  window.extractLast = function(term) {
-    return split(term).pop();
-  };
-
-  window.rstrip = function(val) {
-    if (val) {
-      return val.replace(/\s+$/, '');
-    } else {
-      return val;
-    }
-  };
-
-  // Disable button if text field is empty
-  window.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) {
-    event_name = event_name || 'input';
-    var closest_submit, field;
-    field = $(field_selector);
-    closest_submit = field.closest('form').find(button_selector);
-    if (rstrip(field.val()) === "") {
-      closest_submit.disable();
-    }
-    return field.on(event_name, function() {
-      if (rstrip($(this).val()) === "") {
-        return closest_submit.disable();
-      } else {
-        return closest_submit.enable();
-      }
-    });
-  };
+(function () {
+  document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch);
+  window.addEventListener('hashchange', gl.utils.shiftWindow);
 
-  // Disable button if any input field with given selector is empty
-  window.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
-    var closest_submit, updateButtons;
-    closest_submit = form.find(button_selector);
-    updateButtons = function() {
-      var filled;
-      filled = true;
-      form.find('input').filter(form_selector).each(function() {
-        return filled = rstrip($(this).val()) !== "" || !$(this).attr('required');
-      });
-      if (filled) {
-        return closest_submit.enable();
-      } else {
-        return closest_submit.disable();
-      }
-    };
-    updateButtons();
-    return form.keyup(updateButtons);
-  };
-
-  window.sanitize = function(str) {
-    return str.replace(/<(?:.|\n)*?>/gm, '');
-  };
-
-  window.shiftWindow = function() {
-    return scrollBy(0, -100);
-  };
-
-  document.addEventListener("page:fetch", gl.utils.cleanupBeforeFetch);
-
-  window.addEventListener("hashchange", shiftWindow);
-
-  window.onload = function() {
+  window.onload = function () {
     // Scroll the window to avoid the topnav bar
     // https://github.com/twitter/bootstrap/issues/1768
     if (location.hash) {
-      return setTimeout(shiftWindow, 100);
+      return setTimeout(gl.utils.shiftWindow, 100);
     }
   };
 
-  $(function() {
-    var $body, $document, $sidebarGutterToggle, $window, bootstrapBreakpoint, checkInitialSidebarSize, fitSidebarForSize, flash;
-    $document = $(document);
-    $window = $(window);
-    $body = $('body');
+  $(function () {
+    var $body = $('body');
+    var $document = $(document);
+    var $window = $(window);
+    var $sidebarGutterToggle = $('.js-sidebar-toggle');
+    var $flash = $('.flash-container');
+    var bootstrapBreakpoint = bp.getBreakpointSize();
+    var checkInitialSidebarSize;
+    var fitSidebarForSize;
 
     // Set the default path for all cookies to GitLab's root directory
     Cookies.defaults.path = gon.relative_url_root || '/';
 
     gl.utils.preventDisabledButtons();
-    bootstrapBreakpoint = bp.getBreakpointSize();
-    $(".nav-sidebar").niceScroll({
+    $('.nav-sidebar').niceScroll({
       cursoropacitymax: '0.4',
       cursorcolor: '#FFF',
-      cursorborder: "1px solid #FFF"
+      cursorborder: '1px solid #FFF'
     });
-    $(".js-select-on-focus").on("focusin", function() {
-      return $(this).select().one('mouseup', function(e) {
+    $('.js-select-on-focus').on('focusin', function () {
+      return $(this).select().one('mouseup', function (e) {
         return e.preventDefault();
       });
     // Click a .js-select-on-focus field, select the contents
     // Prevent a mouseup event from deselecting the input
     });
-    $('.remove-row').bind('ajax:success', function() {
+    $('.remove-row').bind('ajax:success', function () {
       $(this).tooltip('destroy')
         .closest('li')
         .fadeOut();
     });
-    $('.js-remove-tr').bind('ajax:before', function() {
+    $('.js-remove-tr').bind('ajax:before', function () {
       return $(this).hide();
     });
-    $('.js-remove-tr').bind('ajax:success', function() {
+    $('.js-remove-tr').bind('ajax:success', function () {
       return $(this).closest('tr').fadeOut();
     });
     $('select.select2').select2({
@@ -180,34 +109,35 @@
       // Initialize select2 selects
       dropdownAutoWidth: true
     });
-    $('.js-select2').bind('select2-close', function() {
-      return setTimeout((function() {
+    $('.js-select2').bind('select2-close', function () {
+      return setTimeout((function () {
         $('.select2-container-active').removeClass('select2-container-active');
         return $(':focus').blur();
       }), 1);
     // Close select2 on escape
     });
     // Initialize tooltips
+    $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover';
     $body.tooltip({
       selector: '.has-tooltip, [data-toggle="tooltip"]',
-      placement: function(_, el) {
+      placement: function (_, el) {
         return $(el).data('placement') || 'bottom';
       }
     });
-    $('.trigger-submit').on('change', function() {
+    $('.trigger-submit').on('change', function () {
       return $(this).parents('form').submit();
     // Form submitter
     });
     gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
     // Flash
-    if ((flash = $(".flash-container")).length > 0) {
-      flash.click(function() {
+    if ($flash.length > 0) {
+      $flash.click(function () {
         return $(this).fadeOut();
       });
-      flash.show();
+      $flash.show();
     }
     // Disable form buttons while a form is submitting
-    $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function(e) {
+    $body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) {
       var buttons;
       buttons = $('[type="submit"]', this);
       switch (e.type) {
@@ -218,36 +148,36 @@
           return buttons.enable();
       }
     });
-    $(document).ajaxError(function(e, xhrObj, xhrSetting, xhrErrorText) {
-      var ref;
+    $(document).ajaxError(function (e, xhrObj) {
+      var ref = xhrObj.status;
       if (xhrObj.status === 401) {
         return new Flash('You need to be logged in.', 'alert');
-      } else if ((ref = xhrObj.status) === 404 || ref === 500) {
+      } else if (ref === 404 || ref === 500) {
         return new Flash('Something went wrong on our end.', 'alert');
       }
     });
-    $('.account-box').hover(function() {
+    $('.account-box').hover(function () {
       // Show/Hide the profile menu when hovering the account box
       return $(this).toggleClass('hover');
     });
-    $document.on('click', '.diff-content .js-show-suppressed-diff', function() {
+    $document.on('click', '.diff-content .js-show-suppressed-diff', function () {
       var $container;
       $container = $(this).parent();
       $container.next('table').show();
       return $container.remove();
     // Commit show suppressed diff
     });
-    $('.navbar-toggle').on('click', function() {
+    $('.navbar-toggle').on('click', function () {
       $('.header-content .title').toggle();
       $('.header-content .header-logo').toggle();
       $('.header-content .navbar-collapse').toggle();
       return $('.navbar-toggle').toggleClass('active');
     });
     // Show/hide comments on diff
-    $body.on("click", ".js-toggle-diff-comments", function(e) {
+    $body.on('click', '.js-toggle-diff-comments', function (e) {
       var $this = $(this);
-      $this.toggleClass('active');
       var notesHolders = $this.closest('.diff-file').find('.notes_holder');
+      $this.toggleClass('active');
       if ($this.hasClass('active')) {
         notesHolders.show().find('.hide').show();
       } else {
@@ -256,30 +186,27 @@
       $this.trigger('blur');
       return e.preventDefault();
     });
-    $document.off("click", '.js-confirm-danger');
-    $document.on("click", '.js-confirm-danger', function(e) {
-      var btn, form, text;
+    $document.off('click', '.js-confirm-danger');
+    $document.on('click', '.js-confirm-danger', function (e) {
+      var btn = $(e.target);
+      var form = btn.closest('form');
+      var text = btn.data('confirm-danger-message');
       e.preventDefault();
-      btn = $(e.target);
-      text = btn.data("confirm-danger-message");
-      form = btn.closest("form");
       return new ConfirmDangerModal(form, text);
     });
-    $document.on('click', 'button', function() {
+    $document.on('click', 'button', function () {
       return $(this).blur();
     });
-    $('input[type="search"]').each(function() {
-      var $this;
-      $this = $(this);
+    $('input[type="search"]').each(function () {
+      var $this = $(this);
       $this.attr('value', $this.val());
     });
-    $document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function(e) {
+    $document.off('keyup', 'input[type="search"]').on('keyup', 'input[type="search"]', function () {
       var $this;
       $this = $(this);
       return $this.attr('value', $this.val());
     });
-    $sidebarGutterToggle = $('.js-sidebar-toggle');
-    $document.off('breakpoint:change').on('breakpoint:change', function(e, breakpoint) {
+    $document.off('breakpoint:change').on('breakpoint:change', function (e, breakpoint) {
       var $gutterIcon;
       if (breakpoint === 'sm' || breakpoint === 'xs') {
         $gutterIcon = $sidebarGutterToggle.find('i');
@@ -288,7 +215,7 @@
         }
       }
     });
-    fitSidebarForSize = function() {
+    fitSidebarForSize = function () {
       var oldBootstrapBreakpoint;
       oldBootstrapBreakpoint = bootstrapBreakpoint;
       bootstrapBreakpoint = bp.getBreakpointSize();
@@ -296,13 +223,13 @@
         return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
       }
     };
-    checkInitialSidebarSize = function() {
+    checkInitialSidebarSize = function () {
       bootstrapBreakpoint = bp.getBreakpointSize();
-      if (bootstrapBreakpoint === "xs" || "sm") {
+      if (bootstrapBreakpoint === 'xs' || 'sm') {
         return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
       }
     };
-    $window.off("resize.app").on("resize.app", function(e) {
+    $window.off('resize.app').on('resize.app', function () {
       return fitSidebarForSize();
     });
     gl.awardsHandler = new AwardsHandler();
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 8bdb0965f994d9efd9fc2e510c47d438e0ebe35b..d7cda977845b00bc680b48f1ceeca6912ecc678c 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,7 +1,7 @@
 /* eslint-disable */
 (function() {
   this.AwardsHandler = (function() {
-    const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence
+    var FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence
     function AwardsHandler() {
       this.aliases = gl.emojiAliases();
       $(document).off('click', '.js-add-award').on('click', '.js-add-award', (function(_this) {
diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6 b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
index e520170ef74f7bfeb72b3c43b543bcdbee91f51d..db9a5a8e40a3e7728e1323b030092223b36e750d 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js.es6
@@ -22,7 +22,7 @@
       fallbackClass: 'is-dragging',
       fallbackOnBody: true,
       ghostClass: 'is-ghost',
-      filter: '.has-tooltip, .btn',
+      filter: '.board-delete, .btn',
       delay: gl.issueBoards.touchEnabled ? 100 : 50,
       scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
       scrollSpeed: 20,
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js
index 230a1b95c525c61b1881baf73f463cde3b15a369..143d21adb37c641aba8fde2fce467ebd94c8f5cd 100644
--- a/app/assets/javascripts/confirm_danger_modal.js
+++ b/app/assets/javascripts/confirm_danger_modal.js
@@ -12,7 +12,7 @@
       submit.disable();
       $('.js-confirm-danger-input').off('input');
       $('.js-confirm-danger-input').on('input', function() {
-        if (rstrip($(this).val()) === project_path) {
+        if (gl.utils.rstrip($(this).val()) === project_path) {
           return submit.enable();
         } else {
           return submit.disable();
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index ff8b8f6d0aea200d3ccf03c6d42f128f501faffc..8e4fd1f19ba396682acf6dbdbe6bfc88787ebf98 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -299,7 +299,7 @@
     };
 
     Dispatcher.prototype.initFieldErrors = function() {
-      $('.show-gl-field-errors').each((i, form) => {
+      $('.gl-show-field-errors').each((i, form) => {
         new gl.GlFieldErrors(form);
       });
     };
diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6
index 31df51ac03a94f5fb0f7876816e5e877e881e7e4..824413bf20fc424d85ee37ec29b890106cd575f9 100644
--- a/app/assets/javascripts/gfm_auto_complete.js.es6
+++ b/app/assets/javascripts/gfm_auto_complete.js.es6
@@ -126,8 +126,8 @@
               }
               return {
                 username: m.username,
-                title: sanitize(title),
-                search: sanitize(m.username + " " + m.name)
+                title: gl.utils.sanitize(title),
+                search: gl.utils.sanitize(m.username + " " + m.name)
               };
             });
           }
@@ -159,7 +159,7 @@
               }
               return {
                 id: i.iid,
-                title: sanitize(i.title),
+                title: gl.utils.sanitize(i.title),
                 search: i.iid + " " + i.title
               };
             });
@@ -189,7 +189,7 @@
               }
               return {
                 id: m.iid,
-                title: sanitize(m.title),
+                title: gl.utils.sanitize(m.title),
                 search: "" + m.title
               };
             });
@@ -222,7 +222,7 @@
               }
               return {
                 id: m.iid,
-                title: sanitize(m.title),
+                title: gl.utils.sanitize(m.title),
                 search: m.iid + " " + m.title
               };
             });
@@ -240,9 +240,9 @@
             var sanitizeLabelTitle;
             sanitizeLabelTitle = function(title) {
               if (/[\w\?&]+\s+[\w\?&]+/g.test(title)) {
-                return "\"" + (sanitize(title)) + "\"";
+                return "\"" + (gl.utils.sanitize(title)) + "\"";
               } else {
-                return sanitize(title);
+                return gl.utils.sanitize(title);
               }
             };
             return $.map(merges, function(m) {
diff --git a/app/assets/javascripts/gl_field_error.js.es6 b/app/assets/javascripts/gl_field_error.js.es6
new file mode 100644
index 0000000000000000000000000000000000000000..f7cbecc0385e9c05779da3a8e7410cbd464b08a5
--- /dev/null
+++ b/app/assets/javascripts/gl_field_error.js.es6
@@ -0,0 +1,164 @@
+/* eslint-disable no-param-reassign */
+((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 `gl-show-field-errors` to the form element, and ensure error messages are
+   * declared in each inputs' `title` attribute. If no title is declared for an invalid
+   * field the user attempts to submit, "This field is required." will be shown by default.
+   *
+   * Opt not to validate certain fields by adding the class `gl-field-error-ignore` to the input.
+   *
+   * Set a custom error anchor for error message to be injected after with the
+   * class `gl-field-error-anchor`
+   *
+   * Examples:
+   *
+   * Basic:
+   *
+   * <form class='gl-show-field-errors'>
+   *  <input type='text' name='username' title='Username is required.'/>
+   * </form>
+   *
+   * Ignore specific inputs (e.g. UsernameValidator):
+   *
+   * <form class='gl-show-field-errors'>
+   *   <div class="form-group>
+   *     <input type='text' class='gl-field-errors-ignore' pattern='[a-zA-Z0-9-_]+'/>
+   *   </div>
+   *   <div class="form-group">
+   *      <input type='text' name='username' title='Username is required.'/>
+   *    </div>
+   * </form>
+   *
+   * Custom Error Anchor (allows error message to be injected after specified element):
+   *
+   * <form class='gl-show-field-errors'>
+   *  <div class="form-group gl-field-error-anchor">
+   *    <input type='text' name='username' title='Username is required.'/>
+   *    // Error message typically injected here
+   *  </div>
+   *  // Error message now injected here
+   * </form>
+   *
+    * */
+
+  /*
+    * Regex Patterns in use:
+    *
+    * Only alphanumeric: : "[a-zA-Z0-9]+"
+    * No special characters : "[a-zA-Z0-9-_]+",
+    *
+    * */
+
+  const errorMessageClass = 'gl-field-error';
+  const inputErrorClass = 'gl-field-error-outline';
+  const errorAnchorSelector = '.gl-field-error-anchor';
+  const ignoreInputSelector = '.gl-field-error-ignore';
+
+  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() {
+      const customErrorAnchor = this.inputElement.parents(errorAnchorSelector);
+      const errorAnchor = customErrorAnchor.length ? customErrorAnchor : this.inputElement;
+
+      // hidden when injected into DOM
+      errorAnchor.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
+      const unignoredSiblings = this.inputElement.siblings(`p:not(${ignoreInputSelector})`);
+      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) {
+        this.renderValid();
+      } else if (this.state.empty) {
+        this.renderEmpty();
+      } else if (!this.state.valid) {
+        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.fieldValidator')
+        .on('keyup.fieldValidator', 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();
+    }
+  }
+
+  global.GlFieldError = GlFieldError;
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/gl_field_errors.js.es6 b/app/assets/javascripts/gl_field_errors.js.es6
index be6c3ec274f17569f87a27cad92350ddf6b91895..6ce392d2a5b54e117f87980da9025971d60ea46e 100644
--- a/app/assets/javascripts/gl_field_errors.js.es6
+++ b/app/assets/javascripts/gl_field_errors.js.es6
@@ -1,131 +1,9 @@
 /* eslint-disable */
-((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();
-      }
-
-    }
+//= require gl_field_error
 
-    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';
+((global) => {
+  const customValidationFlag = 'gl-field-error-ignore';
 
   class GlFieldErrors {
     constructor(form) {
@@ -144,7 +22,7 @@
 
       this.state.inputs = this.form.find(validateSelectors).toArray()
         .filter((input) => !input.classList.contains(customValidationFlag))
-        .map((input) => new GlFieldError({ input, formErrors: this }));
+        .map((input) => new global.GlFieldError({ input, formErrors: this }));
 
       this.form.on('submit', this.catchInvalidFormSubmit);
     }
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index 742807d93ad86cd215b1fe783b596862fa9e6b1d..ce54c34492dbbf984e6e957b6bab0cb42e3456be 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -24,8 +24,8 @@
       if (isNewForm) {
         this.form.find('.div-dropzone').remove();
         this.form.addClass('gfm-form');
-        disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
         // remove notify commit author checkbox for non-commit notes
+        gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
         GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
         new DropzoneInput(this.form);
         autosize(this.textarea);
diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js
index 056baf665259855cefe67adeeedbb198f8bcda4a..e103748d499d04122991e7bcddfeaa3970c1769b 100644
--- a/app/assets/javascripts/graphs/graphs_bundle.js
+++ b/app/assets/javascripts/graphs/graphs_bundle.js
@@ -1,6 +1,6 @@
 /* eslint-disable */
 // This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// Add new JavaScript code in separate files in this directory and they'll automatically
 // be included in the compiled file accessible from http://example.com/assets/application.js
 // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
 // the compiled file.
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index b275620c7996c23bf154136d91ae24b7116192f3..e3c39c895bad004f2a3408470848b9ee99cc20ed 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -6,15 +6,16 @@
     function GroupsSelect() {
       $('.ajax-groups-select').each((function(_this) {
         return function(i, select) {
-          var skip_ldap, skip_groups;
-          skip_ldap = $(select).hasClass('skip_ldap');
+          var all_available, skip_groups;
+          all_available = $(select).data('all-available');
           skip_groups = $(select).data('skip-groups') || [];
           return $(select).select2({
             placeholder: "Search for a group",
             multiple: $(select).hasClass('multiselect'),
             minimumInputLength: 0,
             query: function(query) {
-              return Api.groups(query.term, skip_ldap, skip_groups, function(groups) {
+              options = { all_available: all_available, skip_groups: skip_groups };
+              return Api.groups(query.term, options, function(groups) {
                 var data;
                 data = {
                   results: groups
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 21efe2d76dd11236c0048762ccfd4083b152c938..8447421195d6d4aad88bc80ad33d3889d59e81ff 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -24,6 +24,81 @@
         return null;
       }
     };
+
+    w.gl.utils.ajaxGet = function(url) {
+      return $.ajax({
+        type: "GET",
+        url: url,
+        dataType: "script"
+      });
+    };
+
+    w.gl.utils.split = function(val) {
+      return val.split(/,\s*/);
+    };
+
+    w.gl.utils.extractLast = function(term) {
+      return this.split(term).pop();
+    };
+
+    w.gl.utils.rstrip = function rstrip(val) {
+      if (val) {
+        return val.replace(/\s+$/, '');
+      } else {
+        return val;
+      }
+    };
+
+    w.gl.utils.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) {
+      event_name = event_name || 'input';
+      var closest_submit, field, that;
+      that = this;
+      field = $(field_selector);
+      closest_submit = field.closest('form').find(button_selector);
+      if (this.rstrip(field.val()) === "") {
+        closest_submit.disable();
+      }
+      return field.on(event_name, function() {
+        if (that.rstrip($(this).val()) === "") {
+          return closest_submit.disable();
+        } else {
+          return closest_submit.enable();
+        }
+      });
+    };
+
+    w.gl.utils.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
+      var closest_submit, updateButtons;
+      closest_submit = form.find(button_selector);
+      updateButtons = function() {
+        var filled;
+        filled = true;
+        form.find('input').filter(form_selector).each(function() {
+          return filled = this.rstrip($(this).val()) !== "" || !$(this).attr('required');
+        });
+        if (filled) {
+          return closest_submit.enable();
+        } else {
+          return closest_submit.disable();
+        }
+      };
+      updateButtons();
+      return form.keyup(updateButtons);
+    };
+
+    w.gl.utils.sanitize = function(str) {
+      return str.replace(/<(?:.|\n)*?>/gm, '');
+    };
+
+    w.gl.utils.unbindEvents = function() {
+      return $(document).off('scroll');
+    };
+
+    w.gl.utils.shiftWindow = function() {
+      return w.scrollBy(0, -100);
+    };
+
+
     gl.utils.updateTooltipTitle = function($tooltipEl, newTitle) {
       return $tooltipEl.tooltip('destroy').attr('title', newTitle).tooltip('fixTitle');
     };
diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6
index 371abd09e78c172459e363e3af49d4b8f5819c11..895bc10784ff5ff18439ae17279a5efc1560090d 100644
--- a/app/assets/javascripts/members.js.es6
+++ b/app/assets/javascripts/members.js.es6
@@ -11,7 +11,7 @@
       $('.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);
-      disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
+      gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
     }
 
     removeRow(e) {
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 6658e4811ce1c34dc26baa2440b49d238e9950f4..860ee5df57e02f35aebb37da56367051e969e377 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -238,8 +238,11 @@
               _this.expandViewContainer();
             }
             _this.diffsLoaded = true;
-            _this.scrollToElement("#diffs");
-            _this.highlighSelectedLine();
+            var anchoredDiff = gl.utils.getLocationHash();
+            if (anchoredDiff) _this.openAnchoredDiff(anchoredDiff, function() {
+              _this.scrollToElement("#diffs");
+              _this.highlighSelectedLine();
+            });
             _this.filesCommentButton = $('.files .diff-file').filesCommentButton();
             return $(document).off('click', '.diff-line-num a').on('click', '.diff-line-num a', function(e) {
               e.preventDefault();
@@ -252,6 +255,17 @@
       });
     };
 
+    MergeRequestTabs.prototype.openAnchoredDiff = function(anchoredDiff, cb) {
+      var diffTitle = $('#file-path-' + anchoredDiff);
+      var diffFile = diffTitle.closest('.diff-file');
+      var nothingHereBlock = $('.nothing-here-block:visible', diffFile);
+      if (nothingHereBlock.length) {
+        diffFile.singleFileDiff(true, cb);
+      } else {
+        cb();
+      }
+    };
+
     MergeRequestTabs.prototype.highlighSelectedLine = function() {
       var $diffLine, diffLineTop, hashClassString, locationHash, navBarHeight;
       $('.hll').removeClass('hll');
diff --git a/app/assets/javascripts/network/network_bundle.js b/app/assets/javascripts/network/network_bundle.js
index ede72a96d7681bd3808d5ba2402917f4239dad56..42d6799c82fc956f7ec24f13ce521c9f0a6e049c 100644
--- a/app/assets/javascripts/network/network_bundle.js
+++ b/app/assets/javascripts/network/network_bundle.js
@@ -1,6 +1,6 @@
 /* eslint-disable */
 // This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
+// Add new JavaScript code in separate files in this directory and they'll automatically
 // be included in the compiled file accessible from http://example.com/assets/application.js
 // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
 // the compiled file.
diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js
index 40575caa57fb4b661810be4e226ec7d9c6e4d687..0d3fb31a9cffa9003ecd53013038309660dc75bb 100644
--- a/app/assets/javascripts/project_new.js
+++ b/app/assets/javascripts/project_new.js
@@ -45,7 +45,9 @@
     };
 
     ProjectNew.prototype.toggleRepoVisibility = function () {
-      var $repoAccessLevel = $('.js-repo-access-level select');
+      var $repoAccessLevel = $('.js-repo-access-level select'),
+          containerRegistry = document.querySelectorAll('.js-container-registry')[0],
+          containerRegistryCheckbox = document.getElementById('project_container_registry_enabled');
 
       this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']")
         .nextAll()
@@ -70,8 +72,17 @@
 
           if (selectedVal) {
             this.$repoSelects.removeClass('disabled');
+
+            if (containerRegistry) {
+              containerRegistry.style.display = '';
+            }
           } else {
             this.$repoSelects.addClass('disabled');
+
+            if (containerRegistry) {
+              containerRegistry.style.display = 'none';
+              containerRegistryCheckbox.checked = false;
+            }
           }
         }.bind(this));
     };
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index b74b4ae68ff9a1e696259a5bbdb0b4e7f9f44445..e1acf3c823256347c81b05a2d5d33f13de57318b 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -24,7 +24,7 @@
                   data = groups.concat(projects);
                   return finalCallback(data);
                 };
-                return Api.groups(term, false, false, groupsCallback);
+                return Api.groups(term, {}, groupsCallback);
               };
             } else {
               projectsCallback = finalCallback;
@@ -73,7 +73,7 @@
                     data = groups.concat(projects);
                     return finalCallback(data);
                   };
-                  return Api.groups(query.term, false, false, groupsCallback);
+                  return Api.groups(query.term, {}, groupsCallback);
                 };
               } else {
                 projectsCallback = finalCallback;
diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js
index 6c2389f202ff4ff240fb1cbbc56d526e925753b8..d79e6f014f6b9acc1f18cad78780b56897af3d7b 100644
--- a/app/assets/javascripts/search.js
+++ b/app/assets/javascripts/search.js
@@ -11,7 +11,7 @@
         filterable: true,
         fieldName: 'group_id',
         data: function(term, callback) {
-          return Api.groups(term, false, false, function(data) {
+          return Api.groups(term, {}, function(data) {
             data.unshift({
               name: 'Any'
             });
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index adca76ddd5fd6df090bcee3c2f61a40ad428ba59..8e54ca4f0dcffebcfaa83193c4148abec499342d 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -13,7 +13,7 @@
 
     COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
 
-    function SingleFileDiff(file) {
+    function SingleFileDiff(file, forceLoad, cb) {
       this.file = file;
       this.toggleDiff = bind(this.toggleDiff, this);
       this.content = $('.diff-content', this.file);
@@ -32,9 +32,12 @@
         this.$toggleIcon.addClass('fa-caret-down');
       }
       $('.file-title, .click-to-expand', this.file).on('click', this.toggleDiff);
+      if (forceLoad) {
+        this.toggleDiff(null, cb);
+      }
     }
 
-    SingleFileDiff.prototype.toggleDiff = function(e) {
+    SingleFileDiff.prototype.toggleDiff = function(e, cb) {
       var $target = $(e.target);
       if (!$target.hasClass('file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
       this.isOpen = !this.isOpen;
@@ -54,11 +57,11 @@
         }
       } else {
         this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
-        return this.getContentHTML();
+        return this.getContentHTML(cb);
       }
     };
 
-    SingleFileDiff.prototype.getContentHTML = function() {
+    SingleFileDiff.prototype.getContentHTML = function(cb) {
       this.collapsedContent.hide();
       this.loadingContent.show();
       $.get(this.diffForPath, (function(_this) {
@@ -76,6 +79,8 @@
           if (typeof DiffNotesApp !== 'undefined') {
             DiffNotesApp.compileComponents();
           }
+
+          if (cb) cb();
         };
       })(this));
     };
@@ -84,10 +89,10 @@
 
   })();
 
-  $.fn.singleFileDiff = function() {
+  $.fn.singleFileDiff = function(forceLoad, cb) {
     return this.each(function() {
-      if (!$.data(this, 'singleFileDiff')) {
-        return $.data(this, 'singleFileDiff', new SingleFileDiff(this));
+      if (!$.data(this, 'singleFileDiff') || forceLoad) {
+        return $.data(this, 'singleFileDiff', new SingleFileDiff(this, forceLoad, cb));
       }
     });
   };
diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js
index a18d16ea46ccecbc396ffa4bca7eae4e9e381dd7..cfd1e2204d512b34c084ab58e56dde8f9f940c0d 100644
--- a/app/assets/javascripts/star.js
+++ b/app/assets/javascripts/star.js
@@ -11,11 +11,9 @@
           $this.parent().find('.star-count').text(data.star_count);
           if (isStarred) {
             $starSpan.removeClass('starred').text('Star');
-            gl.utils.updateTooltipTitle($this, 'Star project');
             $starIcon.removeClass('fa-star').addClass('fa-star-o');
           } else {
             $starSpan.addClass('starred').text('Unstar');
-            gl.utils.updateTooltipTitle($this, 'Unstar project');
             $starIcon.removeClass('fa-star-o').addClass('fa-star');
           }
         };
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index 98e301d37993f38ad413e3db5964a32a32d6141f..ce117c3fba5738a332408d15fa87d77ac854fc55 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -1,11 +1,36 @@
-.avatar {
+@mixin avatar-size($size, $margin-right) {
+  width: $size;
+  height: $size;
+  margin-right: $margin-right;
+}
+
+.avatar-container {
   float: left;
-  margin-right: 12px;
+  margin-right: 15px;
+  border-radius: $avatar_radius;
+  border: 1px solid rgba(0, 0, 0, .1);
+  &.s16 { @include avatar-size(16px, 6px); }
+  &.s20 { @include avatar-size(20px, 7px); }
+  &.s24 { @include avatar-size(24px, 8px); }
+  &.s26 { @include avatar-size(26px, 8px); }
+  &.s32 { @include avatar-size(32px, 10px); }
+  &.s36 { @include avatar-size(36px, 10px); }
+  &.s40 { @include avatar-size(40px, 10px); }
+  &.s46 { @include avatar-size(46px, 15px); }
+  &.s48 { @include avatar-size(48px, 10px); }
+  &.s60 { @include avatar-size(60px, 12px); }
+  &.s70 { @include avatar-size(70px, 14px); }
+  &.s90 { @include avatar-size(90px, 15px); }
+  &.s110 { @include avatar-size(110px, 15px); }
+  &.s140 { @include avatar-size(140px, 15px); }
+  &.s160 { @include avatar-size(160px, 20px); }
+}
+
+.avatar {
+  @extend .avatar-container;
   width: 40px;
   height: 40px;
   padding: 0;
-  border-radius: $avatar_radius;
-  border: 1px solid rgba(0, 0, 0, .1);
 
   &.avatar-inline {
     float: none;
@@ -20,22 +45,6 @@
     border-radius: 0;
     border: none;
   }
-
-  &.s16 { width: 16px; height: 16px; margin-right: 6px; }
-  &.s20 { width: 20px; height: 20px; margin-right: 7px; }
-  &.s24 { width: 24px; height: 24px; margin-right: 8px; }
-  &.s26 { width: 26px; height: 26px; margin-right: 8px; }
-  &.s32 { width: 32px; height: 32px; margin-right: 10px; }
-  &.s36 { width: 36px; height: 36px; margin-right: 10px; }
-  &.s40 { width: 40px; height: 40px; margin-right: 10px; }
-  &.s46 { width: 46px; height: 46px; margin-right: 15px; }
-  &.s48 { width: 48px; height: 48px; margin-right: 10px; }
-  &.s60 { width: 60px; height: 60px; margin-right: 12px; }
-  &.s70 { width: 70px; height: 70px; margin-right: 14px; }
-  &.s90 { width: 90px; height: 90px; margin-right: 15px; }
-  &.s110 { width: 110px; height: 110px; margin-right: 15px; }
-  &.s140 { width: 140px; height: 140px; margin-right: 20px; }
-  &.s160 { width: 160px; height: 160px; margin-right: 20px; }
 }
 
 .identicon {
@@ -54,3 +63,17 @@
   &.s140 { font-size: 72px; line-height: 138px; }
   &.s160 { font-size: 96px; line-height: 158px; }
 }
+
+.image-container {
+  @extend .avatar-container;
+  overflow: hidden;
+  display: flex;
+
+  .avatar {
+    border-radius: 0;
+    border: none;
+    height: auto;
+    margin: 0;
+    align-self: center;
+  }
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index c0e9c8bf82914a5fc68b46d4c3a378de594771fa..ed21ad83a1c4d43b687b76d4e6731fa9e33b1b76 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -216,7 +216,7 @@
   svg,
   .fa {
     &:not(:last-child) {
-      margin-right: 3px;
+      margin-right: 5px;
     }
   }
 }
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index baa38ab60c886d8cb640e4181abfde84bc68d776..3e34ec98427952d8bda7297e9374fbd262277c3c 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -36,7 +36,7 @@
   color: $dropdown-toggle-color;
   font-size: 15px;
   text-align: left;
-  border: 1px solid $dropdown-toggle-border-color;
+  border: 1px solid $border-color;
   border-radius: $border-radius-base;
   outline: 0;
   text-overflow: ellipsis;
@@ -45,11 +45,9 @@
 
   .fa {
     position: absolute;
-    top: 50%;
-    right: 6px;
-    margin-top: -6px;
+    top: 10px;
+    right: 8px;
     color: $dropdown-toggle-icon-color;
-    font-size: 10px;
 
     &.fa-spinner {
       font-size: 16px;
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 761c07384f472975ccc2776760640234c3ff559a..f0727e9688ae58d315b10fa26552fbc27956c4ac 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -136,3 +136,35 @@ label {
   color: $red-normal;
 }
 
+.gl-show-field-errors {
+  .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;
+    }
+  }
+
+  .gl-field-success-message {
+    color: $green-normal;
+  }
+
+  .gl-field-error-message {
+    color: $red-normal;
+  }
+
+  .gl-field-hint {
+    color: $gl-text-color;
+  }
+}
+
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 3f877d86a261638aa8607ac8fff12da2a131a529..91ab15034395312c179c0f5e79bd0a1680900547 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
@@ -21,57 +21,66 @@
       background: $color-darker;
     }
 
-    .nav-sidebar li {
-      a {
-        color: $color-light;
-
-        &:hover,
-        &:focus,
-        &:active {
-          background: $color-dark;
-        }
+    .sidebar-header,
+    .sidebar-action-buttons {
+      color: $color-light;
+      background-color: lighten($color-darker, 5%);
+    }
 
-        i {
+    .nav-sidebar {
+      li {
+        a {
           color: $color-light;
-        }
-
-        path,
-        polygon {
-          fill: $color-light;
-        }
 
-        .count {
-          color: $color-light;
-          background: $color-dark;
+          &:hover,
+          &:focus,
+          &:active {
+            background: $color-dark;
+          }
+
+          i {
+            color: $color-light;
+          }
+
+          path,
+          polygon {
+            fill: $color-light;
+          }
+
+          .count {
+            color: $color-light;
+            background: $color-dark;
+          }
+
+          svg {
+            position: relative;
+            top: 3px;
+          }
         }
 
-        svg {
-          position: relative;
-          top: 3px;
+        &.separate-item {
+          border-top: 1px solid $color;
         }
-      }
-
-      &.separate-item {
-        border-top: 1px solid $color;
-      }
 
-      &.active a {
-        color: $white-light;
-        background: $color-dark;
+        &.active a {
+          color: $white-light;
+          background: $color-dark;
 
-        &.no-highlight {
-          border: none;
-        }
+          &.no-highlight {
+            border: none;
+          }
 
-        i {
-          color: $white-light;
-        }
+          i {
+            color: $white-light;
+          }
 
-        path,
-        polygon {
-          fill: $white-light;
+          path,
+          polygon {
+            fill: $white-light;
+          }
         }
       }
+
     }
   }
 }
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 53ee1ed309e1dd7dc7832d89547815216af70146..4993ca7572ae4734587acd9a6f5662d71f618e84 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -49,12 +49,16 @@ header {
         font-size: 18px;
         padding: 0;
         margin: ($header-height - 28) / 2 0;
-        margin-left: 10px;
+        margin-left: 8px;
         height: 28px;
         min-width: 28px;
         line-height: 28px;
         text-align: center;
 
+        &.header-user-dropdown-toggle {
+          margin-left: 14px;
+        }
+
         &:hover,
         &:focus,
         &:active {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 78464af94bd7535293367ab8be6dd2bec9310705..bc0610cc4174f86f6aecafbba48c162fc5fbc518 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -142,10 +142,6 @@ ul.content-list {
       }
     }
 
-    .avatar {
-      margin-right: 15px;
-    }
-
     .controls {
       float: right;
 
diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
index b6f21fd8c91a20904798eee5dadb77d1817c116c..cb2c351c3688506162c967e5e6d062718086ceb1 100644
--- a/app/assets/stylesheets/framework/pagination.scss
+++ b/app/assets/stylesheets/framework/pagination.scss
@@ -7,8 +7,70 @@
   .pagination {
     padding: 0;
   }
+
+  .gap,
+  .gap:hover {
+    background-color: $gray-light;
+    padding: $gl-vert-padding;
+    cursor: default;
+  }
 }
 
 .panel > .gl-pagination {
   margin: 0;
 }
+
+/**
+ * Extra-small screen pagination.
+ */
+@media (max-width: 320px) {
+  .gl-pagination {
+    .first,
+    .last {
+      display: none;
+    }
+
+    .page {
+      display: none;
+
+      &.active {
+        display: inline;
+      }
+    }
+  }
+}
+
+/**
+ * Small screen pagination
+ */
+@media (max-width: $screen-xs) {
+  .gl-pagination {
+    .pagination li a {
+      padding: 6px 10px;
+    }
+
+    .page {
+      display: none;
+
+      &.active {
+        display: inline;
+      }
+    }
+  }
+}
+
+/**
+ * Medium screen pagination
+ */
+@media (min-width: $screen-xs) and (max-width: $screen-md-max) {
+  .gl-pagination {
+    .page {
+      display: none;
+
+      &.active,
+      &.sibling {
+        display: inline;
+      }
+    }
+  }
+}
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index ecdf0be1a05a4532eceaa998d7947026c4b8a6aa..13749f1b7bdbc53a24ffa72b15c7e9dbf2576072 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -27,9 +27,9 @@
         height: 0;
         margin-left: 2px;
         vertical-align: middle;
-        border-top: $caret-width-base dashed;
-        border-right: $caret-width-base solid transparent;
-        border-left: $caret-width-base solid transparent;
+        border-top: 5px dashed;
+        border-right: 5px solid transparent;
+        border-left: 5px solid transparent;
         color: $gray-darkest;
       }
     }
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index c54f7b2757519d48f38a7627f3f128f4e8f4c72f..d74c14ee2a42dc11b0382becd43d99f846b9fdda 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -59,6 +59,11 @@
     padding: 0 !important;
   }
 
+  .sidebar-header {
+    padding: 11px 22px 12px;
+    font-size: 20px;
+  }
+
   li {
     &.separate-item {
       padding-top: 10px;
diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss
index 9282e0ae03becbce23e1dc9044580f9cb0df648a..486ad16ea263aaf88992cc0aae8f45e55974035d 100644
--- a/app/assets/stylesheets/pages/awards.scss
+++ b/app/assets/stylesheets/pages/awards.scss
@@ -1,7 +1,7 @@
 .awards {
   .emoji-icon {
-    width: 20px;
-    height: 20px;
+    width: 19px;
+    height: 19px;
   }
 }
 
@@ -94,7 +94,7 @@
 
 .award-control {
   margin: 3px 5px 3px 0;
-  padding: 6px 5px;
+  padding: 5px 6px;
   outline: 0;
 
   &:hover,
@@ -127,7 +127,7 @@
   .award-control-icon {
     float: left;
     margin-right: 5px;
-    font-size: 20px;
+    font-size: 19px;
   }
 
   .award-control-icon-loading {
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index ef6833c984515283332e8df948ac84f0c1c32cb0..47a7e84b5c600f2ef077921dfcb731cb4a5b2476 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -12,6 +12,10 @@
   opacity: 1!important;
 
   * {
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
     // !important to make sure no style can override this when dragging
     cursor: -webkit-grabbing!important;
     cursor: grabbing!important;
@@ -45,11 +49,6 @@
   .page-with-sidebar {
     padding-bottom: 0;
   }
-
-  .issues-filters {
-    position: relative;
-    z-index: 999999;
-  }
 }
 
 .boards-app {
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index d6a55fbd464bb98e26baf7b411272a6372a4a985..6300ac9662f828df29e6dda4d379b7eef9f77d2c 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -52,10 +52,25 @@
 
 .build-header {
   position: relative;
-  padding-right: 40px;
+  padding: 0;
+  display: flex;
+  min-height: 58px;
+  align-items: center;
 
-  @media (min-width: $screen-sm-min) {
-    padding-right: 0;
+  .btn-inverted {
+    @include btn-outline($white-light, $blue-normal, $blue-normal, $blue-light, $white-light, $blue-light);
+  }
+
+  @media (max-width: $screen-sm-max) {
+    padding-right: 40px;
+
+    .btn-inverted {
+      display: none;
+    }
+  }
+
+  .header-content {
+    flex: 1;
   }
 
   a {
@@ -137,10 +152,15 @@
 
   .retry-link {
     color: $gl-link-color;
+    display: none;
 
     &:hover {
       text-decoration: underline;
     }
+
+    @media (max-width: $screen-sm-max) {
+      display: block;
+    }
   }
 
   .stage-item {
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 52d6a39bd59e27f3d0f55721ad2e932d5a3123b9..98a84351a3d2bf2f6629adbe1bca33718c1318ef 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -164,7 +164,22 @@
 .branch-commit {
   color: $gl-gray;
 
-  .commit-id,
+  .commit-icon {
+    text-align: center;
+    display: inline-block;
+
+    svg {
+      height: 14px;
+      width: 14px;
+      vertical-align: middle;
+      fill: $table-text-gray;
+    }
+  }
+
+  .commit-id {
+    color: $gl-link-color;
+  }
+
   .commit-row-message {
     color: $gl-gray;
   }
diff --git a/app/assets/stylesheets/pages/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss
index 76225ed8d066c56da3aef4b97f8fe2e6762844b7..016bab104eb2b933e480d6f8b00aec7a16532bfd 100644
--- a/app/assets/stylesheets/pages/dashboard.scss
+++ b/app/assets/stylesheets/pages/dashboard.scss
@@ -36,10 +36,6 @@
   }
 }
 
-.dash-project-avatar {
-  float: left;
-}
-
 .dash-project-access-icon {
   float: left;
   margin-right: 5px;
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index cb8cefaca97c503f7457bdf1494a7fe1c1203e69..778126bcfb79037e434a0bb8d653920caa5cb95b 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -55,6 +55,10 @@
     float: left;
   }
 
+  .file-buttons {
+    font-size: 0;
+  }
+
   .select2 {
     float: right;
   }
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index ee2a398f031ff6498b1b797bbc26323f0036b134..4375e29c8db5424f9443086b8fd7eadc17cc01da 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -3,12 +3,14 @@
 }
 
 .dashboard .side .panel .panel-heading .input-group {
+
   .form-control {
     height: 42px;
   }
 }
 
 .group-row {
+
   .stats {
     float: right;
     line-height: $list-text-height;
@@ -21,12 +23,14 @@
 }
 
 .ldap-group-links {
+
   .form-actions {
     margin-bottom: $gl-padding;
   }
 }
 
 .groups-cover-block {
+
   .container-fluid {
     position: relative;
   }
@@ -41,9 +45,14 @@
       background-color: $background-color;
     }
   }
+
+  .group-avatar {
+    border: 0;
+  }
 }
 
 .groups-header {
+
   @media (min-width: $screen-sm-min) {
     .nav-links {
       width: 35%;
diff --git a/app/assets/stylesheets/pages/icons.scss b/app/assets/stylesheets/pages/icons.scss
new file mode 100644
index 0000000000000000000000000000000000000000..407c8db211d64865e59e8c6fbf8d8c4451f393de
--- /dev/null
+++ b/app/assets/stylesheets/pages/icons.scss
@@ -0,0 +1,12 @@
+// CI icon colors
+
+.ci-status-icon {
+  &-created {
+    fill: $gray-darkest;
+  }
+
+  &-skipped,
+  &-canceled {
+    fill: $gl-text-color;
+  }
+}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index a2f5c6c6bd39186e4953de10fc52fbc4846c1a08..10f67b4799809ee06d24b9066ef0234469bd0673 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -75,43 +75,17 @@
     .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 {
+      .username .validation-success {
         color: $green-normal;
       }
 
-      .username .validation-error,
-      .gl-field-error-message {
+      .username .validation-error {
         color: $red-normal;
       }
-
-      .gl-field-hint {
-        color: $gl-text-color;
-      }
-
     }
   }
 
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index b90c91831f2184645cbcd6d7970147e0d5846988..526e9ae5cdda3b73731a19b54d7ee8bff616f4d1 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -105,11 +105,6 @@ ul.notes {
         padding: 2px;
         margin-top: 10px;
       }
-
-      .award-control {
-        font-size: 13px;
-        padding: 2px 5px;
-      }
     }
 
     .note-header {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index a8e8bbcb208f7f7091d629cf934f03e91486d59a..bf3cb6e7ad9defb03b10e41a5ee6d803ca414153 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -85,6 +85,11 @@
   }
 
   .commit-link {
+    a {
+      &:focus {
+        text-decoration: none;
+      }
+    }
 
     .ci-status {
 
@@ -439,7 +444,7 @@
       }
 
       .grouped-pipeline-dropdown {
-        padding: 8px 0;
+        padding: 0;
         width: 186px;
         left: auto;
         right: -197px;
@@ -448,6 +453,14 @@
         ul {
           max-height: 245px;
           overflow: auto;
+
+          li:first-child {
+            padding-top: 8px;
+          }
+
+          li:last-child {
+            padding-bottom: 8px;
+          }
         }
 
         a {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index f0d39b353d2490c950da39d13acb766e6c9f8a46..f7d5456453076c6b7ced775bc0e95e351557a49c 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -96,8 +96,8 @@
 
   .project-avatar {
     float: none;
-    margin-left: auto;
-    margin-right: auto;
+    margin: 0 auto;
+    border: none;
 
     &.identicon {
       border-radius: 50%;
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 01426e28e92eab43272ebb3ef64e5e32cd1804ef..92997eae8b9fcac875bce2b161a3d039aac7237c 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -6,7 +6,8 @@
     white-space: nowrap;
     border-radius: 4px;
 
-    &:hover {
+    &:hover,
+    &:focus {
       text-decoration: none;
     }
 
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index ea76fe188763c6f26e113f0399ebf53dbac281ec..b3aef2fdd328ecb7cfeaf8f4fe0ca3817790cd3d 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -161,3 +161,63 @@
     }
   }
 }
+
+.todos-empty {
+  display: -webkit-flex;
+  display: flex;
+  -webkit-flex-direction: column;
+  flex-direction: column;
+  max-width: 900px;
+  margin-left: auto;
+  margin-right: auto;
+
+  @media (min-width: $screen-sm-min) {
+    -webkit-flex-direction: row;
+    flex-direction: row;
+    padding-top: 80px;
+  }
+}
+
+.todos-empty-content {
+  -webkit-align-self: center;
+  align-self: center;
+  max-width: 480px;
+  margin-right: 20px;
+}
+
+.todos-empty-hero {
+  width: 200px;
+  margin-left: auto;
+  margin-right: auto;
+
+  @media (min-width: $screen-sm-min) {
+    width: 300px;
+    margin-right: 0;
+    -webkit-order: 2;
+    order: 2;
+  }
+}
+
+.todos-all-done {
+  padding-top: 20px;
+
+  @media (min-width: $screen-sm-min) {
+    padding-top: 50px;
+  }
+
+  > svg {
+    display: block;
+    max-width: 300px;
+    margin: 0 auto 20px;
+  }
+
+  p {
+    max-width: 470px;
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  a {
+    font-weight: 600;
+  }
+}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 6ef7cf0bae66bf9b072e5448f0f9c4c5f0327fe4..86e808314f4a81b45e39b22ea58182fdbbb7c905 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -116,8 +116,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
       :metrics_packet_size,
       :send_user_confirmation_email,
       :container_registry_token_expire_delay,
-      :repository_storage,
       :enabled_git_access_protocol,
+      repository_storages: [],
       restricted_visibility_levels: [],
       import_sources: [],
       disabled_oauth_sign_in_sources: []
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index f35f4a8c8112552dc8ac4fe3df822066f6735b23..bb912ed10cca4a264e8b64ca54036e14cd341ded 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -3,7 +3,7 @@ class Admin::UsersController < Admin::ApplicationController
 
   def index
     @users = User.order_name_asc.filter(params[:filter])
-    @users = @users.search(params[:name]) if params[:name].present?
+    @users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present?
     @users = @users.sort(@sort = params[:sort])
     @users = @users.page(params[:page])
   end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 4f855134368f6c0d3234dd3d08484403b0e6ba71..42fd09e9b7e0250d96e202b51ec9d22f2987f720 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -126,7 +126,7 @@ class Projects::LabelsController < Projects::ApplicationController
   alias_method :subscribable_resource, :label
 
   def find_labels
-    @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute.includes(:priorities)
+    @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute
   end
 
   def authorize_admin_labels!
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index d08f490de180322b685071d97aa3efe5374030c1..699a56ae2f879ef3556d552285cde5288b2effe6 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -25,18 +25,15 @@ class Projects::ProjectMembersController < Projects::ApplicationController
   end
 
   def create
-    if params[:user_ids].blank?
-      return redirect_to(namespace_project_project_members_path(@project.namespace, @project), alert: 'No users or groups specified.')
-    end
+    status = Members::CreateService.new(@project, current_user, params).execute
 
-    @project.team.add_users(
-      params[:user_ids].split(','),
-      params[:access_level],
-      expires_at: params[:expires_at],
-      current_user: current_user
-    )
+    redirect_url = namespace_project_project_members_path(@project.namespace, @project)
 
-    redirect_to namespace_project_project_members_path(@project.namespace, @project), notice: 'Users were successfully added.'
+    if status
+      redirect_to redirect_url, notice: 'Users were successfully added.'
+    else
+      redirect_to redirect_url, alert: 'No users or groups specified.'
+    end
   end
 
   def update
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 8c148ecfaeb12ea0806f026d28f92d8f7a17d144..bce5e29d8d81a6ba704391b1bb4223f856cc5b33 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -289,7 +289,8 @@ class ProjectsController < Projects::ApplicationController
       render 'projects/empty' if @project.empty_repo?
     else
       if @project.wiki_enabled?
-        @wiki_home = @project.wiki.find_page('home', params[:version_id])
+        @project_wiki = @project.wiki
+        @wiki_home = @project_wiki.find_page('home', params[:version_id])
       elsif @project.feature_available?(:issues, current_user)
         @issues = issues_collection
         @issues = @issues.page(params[:page])
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index e27986ef95b382e70ac7120a81aca5f553b1c82d..cc2073081b54a0f27be61ee32e7f0e7d731fa4e2 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -126,7 +126,7 @@ class IssuableFinder
 
     @labels =
       if labels? && !filter_by_no_label?
-        LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute
+        LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute(skip_authorization: true)
       else
         Label.none
       end
@@ -273,7 +273,7 @@ class IssuableFinder
         items = items.with_label(label_names, params[:sort])
 
         if projects
-          label_ids = LabelsFinder.new(current_user, project_ids: projects).execute.select(:id)
+          label_ids = LabelsFinder.new(current_user, project_ids: projects).execute(skip_authorization: true).select(:id)
           items = items.where(labels: { id: label_ids })
         end
       end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 6229384817b74115b9736c4194aa2212ec7ee4b2..45a567a1eba17bf8f2d5803e32fe920e7354eb0b 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -93,11 +93,11 @@ module ApplicationSettingsHelper
     end
   end
 
-  def repository_storage_options_for_select
+  def repository_storages_options_for_select
     options = Gitlab.config.repositories.storages.map do |name, path|
       ["#{name} - #{path}", name]
     end
 
-    options_for_select(options, @application_setting.repository_storage)
+    options_for_select(options, @application_setting.repository_storages)
   end
 end
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index b7f48630bd4b7aab25037a47521089a6726d97f0..fabe5c1f63a168ddf2f9d0dd0bd889b59cb3cf5e 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -47,8 +47,10 @@ module CiStatusHelper
         'icon_play'
       when 'created'
         'icon_status_created'
+      when 'skipped'
+        'icon_status_skipped'
       else
-        'icon_status_cancel'
+        'icon_status_canceled'
       end
 
     custom_icon(icon_name)
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index 81e0b6bb5ae82828f61044f0d36fc6da15c66ad8..cbab1fd5967c90ea15f7c2d618b4abdc144c3442 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -43,7 +43,7 @@ module DropdownsHelper
     default_label = data_attr[:default_label]
     content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do
       output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}")
-      output << icon('chevron-down')
+      output << icon('caret-down')
       output.html_safe
     end
   end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index f8ded05c31a4efe93ed21072178a3360764bec16..00e640764080825ef1fd3e53d95a0b85a130a0af 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -39,6 +39,12 @@ module EventsHelper
     end
   end
 
+  def event_filter_visible(feature_key)
+    return true unless @project
+
+    @project.feature_available?(feature_key, current_user)
+  end
+
   def event_preposition(event)
     if event.push? || event.commented? || event.target
       "at"
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index c99aa7772bba642af874e445b5bea7938d2b9afd..6e7a90e7d9c902e7577d3c254db8d9d4da4ae840 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -18,6 +18,7 @@ class ApplicationSetting < ActiveRecord::Base
   serialize :disabled_oauth_sign_in_sources, Array
   serialize :domain_whitelist, Array
   serialize :domain_blacklist, Array
+  serialize :repository_storages
 
   cache_markdown_field :sign_in_text
   cache_markdown_field :help_page_text
@@ -74,9 +75,8 @@ class ApplicationSetting < ActiveRecord::Base
             presence: true,
             numericality: { only_integer: true, greater_than: 0 }
 
-  validates :repository_storage,
-    presence: true,
-    inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
+  validates :repository_storages, presence: true
+  validate :check_repository_storages
 
   validates :enabled_git_access_protocol,
             inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true }
@@ -166,7 +166,7 @@ class ApplicationSetting < ActiveRecord::Base
       disabled_oauth_sign_in_sources: [],
       send_user_confirmation_email: false,
       container_registry_token_expire_delay: 5,
-      repository_storage: 'default',
+      repository_storages: ['default'],
       user_default_external: false,
     )
   end
@@ -201,6 +201,29 @@ class ApplicationSetting < ActiveRecord::Base
     self.domain_blacklist_raw = file.read
   end
 
+  def repository_storages
+    value = read_attribute(:repository_storages)
+    value = [value] if value.is_a?(String)
+    value = [] if value.nil?
+
+    value
+  end
+
+  # repository_storage is still required in the API. Remove in 9.0
+  def repository_storage
+    repository_storages.first
+  end
+
+  def repository_storage=(value)
+    self.repository_storages = [value]
+  end
+
+  # Choose one of the available repository storage options. Currently all have
+  # equal weighting.
+  def pick_repository_storage
+    repository_storages.sample
+  end
+
   def runners_registration_token
     ensure_runners_registration_token!
   end
@@ -208,4 +231,12 @@ class ApplicationSetting < ActiveRecord::Base
   def health_check_access_token
     ensure_health_check_access_token!
   end
+
+  private
+
+  def check_repository_storages
+    invalid = repository_storages - Gitlab.config.repositories.storages.keys
+    errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless
+      invalid.empty?
+  end
 end
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index 9216122923e4bf22ab79d03f38a1ef1c870962b4..6d88951c7135bd3c9322dba937272ed9626c7388 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -31,7 +31,7 @@ module ProjectFeaturesCompatibility
   def write_feature_attribute(field, value)
     build_project_feature unless project_feature
 
-    access_level = value == "true" ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
+    access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
     project_feature.update_attribute(field, access_level)
   end
 end
diff --git a/app/models/group_label.rb b/app/models/group_label.rb
index a698b532d19a41f0dcb10fcec6236385ed352220..68841ace2e6b62698c6f5d73e66eab45c360cb27 100644
--- a/app/models/group_label.rb
+++ b/app/models/group_label.rb
@@ -5,6 +5,10 @@ class GroupLabel < Label
 
   alias_attribute :subject, :group
 
+  def subject_foreign_key
+    'group_id'
+  end
+
   def to_reference(source_project = nil, target_project = nil, format: :id)
     super(source_project, target_project, format: format)
   end
diff --git a/app/models/label.rb b/app/models/label.rb
index 149fd98ecb3b730dd7c700b3ae391c9340228790..d9287f2dc290e328003c24a5148fa5dfe591c145 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -92,16 +92,23 @@ class Label < ActiveRecord::Base
     nil
   end
 
-  def open_issues_count(user = nil, project = nil)
-    issues_count(user, project_id: project.try(:id) || project_id, state: 'opened')
+  def open_issues_count(user = nil)
+    issues_count(user, state: 'opened')
   end
 
-  def closed_issues_count(user = nil, project = nil)
-    issues_count(user, project_id: project.try(:id) || project_id, state: 'closed')
+  def closed_issues_count(user = nil)
+    issues_count(user, state: 'closed')
   end
 
-  def open_merge_requests_count(user = nil, project = nil)
-    merge_requests_count(user, project_id: project.try(:id) || project_id, state: 'opened')
+  def open_merge_requests_count(user = nil)
+    params = {
+      subject_foreign_key => subject.id,
+      label_name: title,
+      scope: 'all',
+      state: 'opened'
+    }
+
+    MergeRequestsFinder.new(user, params.with_indifferent_access).execute.count
   end
 
   def prioritize!(project, value)
@@ -167,15 +174,8 @@ class Label < ActiveRecord::Base
   end
 
   def issues_count(user, params = {})
-    IssuesFinder.new(user, params.reverse_merge(label_name: title, scope: 'all'))
-                .execute
-                .count
-  end
-
-  def merge_requests_count(user, params = {})
-    MergeRequestsFinder.new(user, params.reverse_merge(label_name: title, scope: 'all'))
-                       .execute
-                       .count
+    params.merge!(subject_foreign_key => subject.id, label_name: title, scope: 'all')
+    IssuesFinder.new(user, params.with_indifferent_access).execute.count
   end
 
   def label_format_reference(format = :id)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 0397c57f9355e07414e81e60f80c9663e187a5d6..6b8ac3fb48b9abc2d990aaa497125b77f0bd0de6 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -441,11 +441,11 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def should_remove_source_branch?
-    merge_params['should_remove_source_branch'].present?
+    Gitlab::Utils.to_boolean(merge_params['should_remove_source_branch'])
   end
 
   def force_remove_source_branch?
-    merge_params['force_remove_source_branch'].present?
+    Gitlab::Utils.to_boolean(merge_params['force_remove_source_branch'])
   end
 
   def remove_source_branch?
diff --git a/app/models/project.rb b/app/models/project.rb
index ae57815c6209b453c8bba438000cf9367cb28316..cf931f64c0313e6a95982ab8011fab59a2658467 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -28,8 +28,13 @@ class Project < ActiveRecord::Base
   default_value_for :archived, false
   default_value_for :visibility_level, gitlab_config_features.visibility_level
   default_value_for :container_registry_enabled, gitlab_config_features.container_registry
-  default_value_for(:repository_storage) { current_application_settings.repository_storage }
+  default_value_for(:repository_storage) { current_application_settings.pick_repository_storage }
   default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled }
+  default_value_for :issues_enabled, gitlab_config_features.issues
+  default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
+  default_value_for :builds_enabled, gitlab_config_features.builds
+  default_value_for :wiki_enabled, gitlab_config_features.wiki
+  default_value_for :snippets_enabled, gitlab_config_features.snippets
 
   after_create :ensure_dir_exist
   after_create :create_project_feature, unless: :project_feature
@@ -390,7 +395,7 @@ class Project < ActiveRecord::Base
     end
 
     def group_ids
-      joins(:namespace).where(namespaces: { type: 'Group' }).pluck(:namespace_id)
+      joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id)
     end
   end
 
diff --git a/app/models/project_label.rb b/app/models/project_label.rb
index 33c2b6177155755c6d4aacf64cca323869429980..82f47f0e8fd407b96abb1320019fc8cb951c7ea1 100644
--- a/app/models/project_label.rb
+++ b/app/models/project_label.rb
@@ -12,6 +12,10 @@ class ProjectLabel < Label
 
   alias_attribute :subject, :project
 
+  def subject_foreign_key
+    'project_id'
+  end
+
   def to_reference(target_project = nil, format: :id)
     super(project, target_project, format: format)
   end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 5bcf199d46814a5ad0be807c4956a33a1859d3ba..0a493b7a12be00ed0ee827bad9986de98f9f99c7 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -237,7 +237,7 @@ class JiraService < IssueTrackerService
   end
 
   def resource_url(resource)
-    "#{Settings.gitlab['url'].chomp("/")}#{resource}"
+    "#{Settings.gitlab.base_url.chomp("/")}#{resource}"
   end
 
   def build_entity_url(entity_name, entity_id)
diff --git a/app/models/user.rb b/app/models/user.rb
index e2a97c3a757598d58344b82e894debd7b58d9cd7..af3c0b7dc02e8ea4ae186d01085a04383b87f41b 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -258,6 +258,24 @@ class User < ActiveRecord::Base
       )
     end
 
+    # searches user by given pattern
+    # it compares name, email, username fields and user's secondary emails with given pattern
+    # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
+
+    def search_with_secondary_emails(query)
+      table = arel_table
+      email_table = Email.arel_table
+      pattern = "%#{query}%"
+      matched_by_emails_user_ids = email_table.project(email_table[:user_id]).where(email_table[:email].matches(pattern))
+
+      where(
+        table[:name].matches(pattern).
+          or(table[:email].matches(pattern)).
+          or(table[:username].matches(pattern)).
+          or(table[:id].in(matched_by_emails_user_ids))
+      )
+    end
+
     def by_login(login)
       return nil unless login
 
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index fbb3d4507d6e4b16af38100e3b05bec539ec0ef7..1ee31023e26628b6f184d1b76c36dbe8372ef3f2 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -2,11 +2,11 @@ class ProjectPolicy < BasePolicy
   def rules
     team_access!(user)
 
-    owner = user.admin? ||
-            project.owner == user ||
+    owner = project.owner == user ||
             (project.group && project.group.has_owner?(user))
 
-    owner_access! if owner
+    owner_access! if user.admin? || owner
+    team_member_owner_access! if owner
 
     if project.public? || (project.internal? && !user.external?)
       guest_access!
@@ -16,7 +16,7 @@ class ProjectPolicy < BasePolicy
       can! :read_build if project.public_builds?
 
       if project.request_access_enabled &&
-         !(owner || project.team.member?(user) || project_group_member?(user))
+         !(owner || user.admin? || project.team.member?(user) || project_group_member?(user))
         can! :request_access
       end
     end
@@ -135,6 +135,10 @@ class ProjectPolicy < BasePolicy
     can! :destroy_issue
   end
 
+  def team_member_owner_access!
+    team_member_reporter_access!
+  end
+
   # Push abilities on the users team role
   def team_access!(user)
     access = project.team.max_member_access(user.id)
diff --git a/app/services/members/create_service.rb b/app/services/members/create_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e4b24ccef92c834436f25d04da4f792cc80ae8ae
--- /dev/null
+++ b/app/services/members/create_service.rb
@@ -0,0 +1,16 @@
+module Members
+  class CreateService < BaseService
+    def execute
+      return false if params[:user_ids].blank?
+
+      project.team.add_users(
+        params[:user_ids].split(','),
+        params[:access_level],
+        expires_at: params[:expires_at],
+        current_user: current_user
+      )
+
+      true
+    end
+  end
+end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index 404f75616b542072b90160ae0c7c201604312783..f415244068b4345edd9d5640f4b7a87e5d0d8a4f 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -13,20 +13,8 @@ module MergeRequests
       merge_request.target_project ||= (project.forked_from_project || project)
       merge_request.target_branch ||= merge_request.target_project.default_branch
 
-      if merge_request.target_branch.blank? || merge_request.source_branch.blank?
-        message =
-          if params[:source_branch] || params[:target_branch]
-            "You must select source and target branch"
-          end
-
-        return build_failed(merge_request, message)
-      end
-
-      if merge_request.source_project == merge_request.target_project &&
-         merge_request.target_branch == merge_request.source_branch
-
-        return build_failed(merge_request, 'You must select different branches')
-      end
+      messages = validate_branches(merge_request)
+      return build_failed(merge_request, messages) unless messages.empty?
 
       compare = CompareService.new.execute(
         merge_request.source_project,
@@ -43,6 +31,34 @@ module MergeRequests
 
     private
 
+    def validate_branches(merge_request)
+      messages = []
+
+      if merge_request.target_branch.blank? || merge_request.source_branch.blank?
+        messages <<
+          if params[:source_branch] || params[:target_branch]
+            "You must select source and target branch"
+          end
+      end
+
+      if merge_request.source_project == merge_request.target_project &&
+         merge_request.target_branch == merge_request.source_branch
+
+        messages << 'You must select different branches'
+      end
+
+      # See if source and target branches exist
+      unless merge_request.source_project.commit(merge_request.source_branch)
+        messages << "Source branch \"#{merge_request.source_branch}\" does not exist"
+      end
+
+      unless merge_request.target_project.commit(merge_request.target_branch)
+        messages << "Target branch \"#{merge_request.target_branch}\" does not exist"
+      end
+
+      messages
+    end
+
     # When your branch name starts with an iid followed by a dash this pattern will be
     # interpreted as the user wants to close that issue on this project.
     #
@@ -91,8 +107,10 @@ module MergeRequests
       merge_request
     end
 
-    def build_failed(merge_request, message)
-      merge_request.errors.add(:base, message) unless message.nil?
+    def build_failed(merge_request, messages)
+      messages.compact.each do |message|
+        merge_request.errors.add(:base, message)
+      end
       merge_request.compare_commits = []
       merge_request.can_be_created = false
       merge_request
diff --git a/app/views/admin/appearances/preview.html.haml b/app/views/admin/appearances/preview.html.haml
index acbe17036f78333b2fa36a938c37b1ca3f91721d..1af7dd5bb67f8f63488ada530f7a2e8f2efd127c 100644
--- a/app/views/admin/appearances/preview.html.haml
+++ b/app/views/admin/appearances/preview.html.haml
@@ -1,6 +1,6 @@
 = render 'devise/shared/tab_single', tab_title: 'Sign in preview'
 .login-box
-  %form.show-gl-field-errors
+  %form.gl-show-field-errors
     .form-group
       = label_tag :login
       = text_field_tag :login, nil, class: "form-control top", title: 'Please provide your username or email address.'
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index c4c68cd789150cc2c93105d6cefc677aa9ceaf87..28003e5f5091dcf647261b80c34f0b86d0227b58 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -353,9 +353,9 @@
   %fieldset
     %legend Repository Storage
     .form-group
-      = f.label :repository_storage, 'Storage path for new projects', class: 'control-label col-sm-2'
+      = f.label :repository_storages, 'Storage paths for new projects', class: 'control-label col-sm-2'
       .col-sm-10
-        = f.select :repository_storage, repository_storage_options_for_select, {}, class: 'form-control'
+        = f.select :repository_storages, repository_storages_options_for_select, {include_hidden: false}, multiple: true, class: 'form-control'
         .help-block
           Manage repository storage paths. Learn more in the
           = succeed "." do
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index adfa1eaafc97e37bc708c90a37b693608f2450a2..05c88ca1cc86542b5b9d24b533c1c20ae31961df 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -16,7 +16,8 @@
     %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
       = visibility_level_icon(group.visibility_level, fw: false)
 
-  = image_tag group_icon(group), class: "avatar s40 hidden-xs"
+  .image-container.s40
+    = image_tag group_icon(group), class: "avatar s40 hidden-xs"
   .title
     = link_to [:admin, group], class: 'group-name' do
       = group.name
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 0188ed448ce7c33bbab736254c7bde81f9c55317..a7c1a4f5038c8a3a284126eb03bcd8daac19f88c 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -13,7 +13,8 @@
         Group info:
       %ul.well-list
         %li
-          = image_tag group_icon(@group), class: "avatar s60"
+          .image-container.s60
+            = image_tag group_icon(@group), class: "avatar s60"
         %li
           %span.light Name:
           %strong= @group.name
diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml
index 676812121d773d2901c1b7ca0e3237975a53bbaf..824edd171f3672d3192f5a6b0eb67ffe625b9ef9 100644
--- a/app/views/admin/logs/show.html.haml
+++ b/app/views/admin/logs/show.html.haml
@@ -1,7 +1,7 @@
 - @no_container = true
 - page_title "Logs"
 - loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
-             Gitlab::ProductionLogger, Gitlab::SidekiqLogger,
+             Gitlab::EnvironmentLogger, Gitlab::SidekiqLogger,
              Gitlab::RepositoryCheckLogger]
 = render 'admin/background_jobs/head'
 
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 339cfc613fe8aff0b309e307d9939e35f9121059..10dce6f3d8fdcf5105fffeaa3ff3c444ac252463 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -76,7 +76,8 @@
             .title
               = link_to [:admin, project.namespace.becomes(Namespace), project] do
                 .dash-project-avatar
-                  = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+                  .image-container.s40
+                    = project_icon(project, alt: '', class: 'avatar project-avatar s40')
                 %span.project-full-name
                   %span.namespace-name
                     - if project.namespace
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 357123c2c134407422c612aa9636c32745feeaa6..d3038ae644f8210b19933d4c99efa207d0a03c7b 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -10,7 +10,7 @@
           = hidden_field_tag "filter", h(params[:filter])
         .search-holder
           .search-field-holder
-            = search_field_tag :name, params[:name], placeholder: 'Search by name, email or username', class: 'form-control search-text-input js-search-input', spellcheck: false
+            = search_field_tag :search_query, params[:search_query], placeholder: 'Search by name, email or username', class: 'form-control search-text-input js-search-input', spellcheck: false
             = icon("search", class: "search-icon")
           .dropdown
             - toggle_text = if @sort.present? then sort_options_hash[@sort] else sort_title_name end
diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder
index 0404d0728ea116a9adb8c409657112314058a623..bdea1064096b32f874a9ddcd660d6a6bf2af063d 100644
--- a/app/views/dashboard/issues.atom.builder
+++ b/app/views/dashboard/issues.atom.builder
@@ -1,7 +1,7 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "#{current_user.name} issues"
-  xml.link    href: issues_dashboard_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+  xml.link    href: url_for(params), rel: "self", type: "application/atom+xml"
   xml.link    href: issues_dashboard_url, rel: "alternate", type: "text/html"
   xml.id      issues_dashboard_url
   xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 1eec4db45a07be6dce1bac2b8b3f58f28061d459..3caaf827ff5ad82559e7ea13c2aeb714fad24760 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -2,13 +2,13 @@
 - header_title  "Issues", issues_dashboard_path(assignee_id: current_user.id)
 = content_for :meta_tags do
   - if current_user
-    = auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
+    = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{current_user.name} issues")
 
 .top-area
   = render 'shared/issuable/nav', type: :issues
   .nav-controls
     - if current_user
-      = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do
+      = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do
         = icon('rss')
         %span.icon-label
           Subscribe
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 2a0302638baaca2a01d3cbb8d75e4172721d2348..2411cc457240b7a2dfbb1eafbdecacebd867f090 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -1,69 +1,70 @@
 - page_title "Todos"
 - header_title "Todos", dashboard_todos_path
 
-.top-area
-  %ul.nav-links
-    - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending')
-    %li{class: "todos-pending #{todo_pending_active}"}
-      = link_to todos_filter_path(state: 'pending') do
-        %span
-          To do
-        %span.badge
-          = number_with_delimiter(todos_pending_count)
-    - todo_done_active = ('active' if params[:state] == 'done')
-    %li{class: "todos-done #{todo_done_active}"}
-      = link_to todos_filter_path(state: 'done') do
-        %span
-          Done
-        %span.badge
-          = number_with_delimiter(todos_done_count)
+- if current_user.todos.any?
+  .top-area
+    %ul.nav-links
+      - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending')
+      %li{class: "todos-pending #{todo_pending_active}"}
+        = link_to todos_filter_path(state: 'pending') do
+          %span
+            To do
+          %span.badge
+            = number_with_delimiter(todos_pending_count)
+      - todo_done_active = ('active' if params[:state] == 'done')
+      %li{class: "todos-done #{todo_done_active}"}
+        = link_to todos_filter_path(state: 'done') do
+          %span
+            Done
+          %span.badge
+            = number_with_delimiter(todos_done_count)
 
-  .nav-controls
-    - if @todos.any?(&:pending?)
-      = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn btn-loading js-todos-mark-all', method: :delete do
-        Mark all as done
-        = icon('spinner spin')
+    .nav-controls
+      - if @todos.any?(&:pending?)
+        = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn btn-loading js-todos-mark-all', method: :delete do
+          Mark all as done
+          = icon('spinner spin')
 
-.todos-filters
-  .row-content-block.second-block
-    = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form' do
-      .filter-item.inline
-        - if params[:project_id].present?
-          = hidden_field_tag(:project_id, params[:project_id])
-        = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
-          placeholder: 'Search projects', data: { data: todo_projects_options } })
-      .filter-item.inline
-        - if params[:author_id].present?
-          = hidden_field_tag(:author_id, params[:author_id])
-        = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
-          placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author' } })
-      .filter-item.inline
-        - if params[:type].present?
-          = hidden_field_tag(:type, params[:type])
-        = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit',
-          data: { data: todo_types_options } })
-      .filter-item.inline.actions-filter
-        - if params[:action_id].present?
-          = hidden_field_tag(:action_id, params[:action_id])
-        = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit',
-          data: { data: todo_actions_options }})
-      .pull-right
-        .dropdown.inline.prepend-left-10
-          %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
-            %span.light
-            - if @sort.present?
-              = sort_options_hash[@sort]
-            - else
-              = sort_title_recently_created
-            = icon('caret-down')
-          %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
-            %li
-              = link_to todos_filter_path(sort: sort_value_priority) do
-                = sort_title_priority
-              = link_to todos_filter_path(sort: sort_value_recently_created) do
+  .todos-filters
+    .row-content-block.second-block
+      = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form' do
+        .filter-item.inline
+          - if params[:project_id].present?
+            = hidden_field_tag(:project_id, params[:project_id])
+          = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
+            placeholder: 'Search projects', data: { data: todo_projects_options } })
+        .filter-item.inline
+          - if params[:author_id].present?
+            = hidden_field_tag(:author_id, params[:author_id])
+          = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit',
+            placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author' } })
+        .filter-item.inline
+          - if params[:type].present?
+            = hidden_field_tag(:type, params[:type])
+          = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit',
+            data: { data: todo_types_options } })
+        .filter-item.inline.actions-filter
+          - if params[:action_id].present?
+            = hidden_field_tag(:action_id, params[:action_id])
+          = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit',
+            data: { data: todo_actions_options }})
+        .pull-right
+          .dropdown.inline.prepend-left-10
+            %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+              %span.light
+              - if @sort.present?
+                = sort_options_hash[@sort]
+              - else
                 = sort_title_recently_created
-              = link_to todos_filter_path(sort: sort_value_oldest_created) do
-                = sort_title_oldest_created
+              = icon('caret-down')
+            %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
+              %li
+                = link_to todos_filter_path(sort: sort_value_priority) do
+                  = sort_title_priority
+                = link_to todos_filter_path(sort: sort_value_recently_created) do
+                  = sort_title_recently_created
+                = link_to todos_filter_path(sort: sort_value_oldest_created) do
+                  = sort_title_oldest_created
 
 
 .prepend-top-default
@@ -78,5 +79,29 @@
         %ul.content-list.todos-list
           = render group[1]
     = paginate @todos, theme: "gitlab"
+  - elsif current_user.todos.any?
+    .todos-all-done
+      = render "shared/empty_states/todos_all_done.svg"
+      %h4.text-center
+        Good job! Looks like you don't have any todos left.
+      %p.text-center
+        Are you looking for things to do? Take a look at
+        = succeed "," do
+          = link_to "the opened issues", issues_dashboard_path
+        contribute to
+        = link_to "merge requests", merge_requests_dashboard_path
+        or mention someone in a comment to assign a new todo automatically.
   - else
-    .nothing-here-block You're all done!
+    .todos-empty
+      .todos-empty-hero
+        = render "shared/empty_states/todos_empty.svg"
+      .todos-empty-content
+        %h4
+          Todos let you see what you should do next.
+        %p
+          When an issue or merge request is assigned to you, or when you
+          %strong
+            @mention
+          in a comment, this will trigger a new item in your todo list, automatically.
+        %p
+          You will always know what to work on next.
diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml
index 5d25dd398d6a9741b6f38aa87f8f47c047d49877..73e70dc63e56355e8310582acafb6d7642699a9b 100644
--- a/app/views/devise/confirmations/new.html.haml
+++ b/app/views/devise/confirmations/new.html.haml
@@ -1,7 +1,7 @@
 = render 'devise/shared/tab_single', tab_title: 'Resend confirmation instructions'
 .login-box
   .login-body
-    = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: 'show-gl-field-errors' }) do |f|
+    = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f|
       .devise-errors
         = devise_error_messages!
       .form-group
diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
index b518fae7c9598b2c722e45b3f8e4e817853123c3..5e189e6dc544af58514ed2ae1654d5262e0d83c0 100644
--- a/app/views/devise/passwords/edit.html.haml
+++ b/app/views/devise/passwords/edit.html.haml
@@ -1,7 +1,7 @@
 = render 'devise/shared/tab_single', tab_title:'Change your password'
 .login-box
   .login-body
-    = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'show-gl-field-errors' }) do |f|
+    = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'gl-show-field-errors' }) do |f|
       .devise-errors
         = devise_error_messages!
       = f.hidden_field :reset_password_token
diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml
index 1fcfd06419a55679799f8045c35c9059262acc7b..99ce13adf7408f47e00a27c2e32b5b00396ec0be 100644
--- a/app/views/devise/passwords/new.html.haml
+++ b/app/views/devise/passwords/new.html.haml
@@ -1,7 +1,7 @@
 = render 'devise/shared/tab_single', tab_title: 'Reset Password'
 .login-box
   .login-body
-    = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'show-gl-field-errors' }) do |f|
+    = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f|
       .devise-errors
         = devise_error_messages!
       .form-group
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 5fd896f6835db349c665e3d165e528fd07cb77f6..21b895808189e9210f319f786d71b91606922b3b 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -1,4 +1,4 @@
-= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user show-gl-field-errors', 'aria-live' => 'assertive'}) do |f|
+= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-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."
diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml
index 1d381ad78932a6a1bd623a9ea2982efedd5b9c7d..a6cadbcbdff8470a95fb20b355eb6fce02e308bd 100644
--- a/app/views/devise/sessions/_new_crowd.html.haml
+++ b/app/views/devise/sessions/_new_crowd.html.haml
@@ -1,4 +1,4 @@
-= form_tag(omniauth_authorize_path(:user, :crowd), id: 'new_crowd_user', class: 'show-gl-field-errors') do
+= form_tag(omniauth_authorize_path(:user, :crowd), id: 'new_crowd_user', class: 'gl-show-field-errors') do
   .form-group
     = label_tag :username, 'Username or email'
     = text_field_tag :username, nil, {class: "form-control top", title: "This field is required", autofocus: "autofocus", required: true }
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index c18bc2ac413feeee93f03f1b195fae9f4132f911..3ab5461f92933087e768d77953b5ec0e216f4e4c 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -1,4 +1,4 @@
-= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user', class: "show-gl-field-errors") do
+= form_tag(omniauth_callback_path(:user, server['provider_name']), id: 'new_ldap_user', class: "gl-show-field-errors") do
   .form-group
     = label_tag :username, "#{server['label']} Username"
     = text_field_tag :username, nil, {class: "form-control top", title: "This field is required.", autofocus: "autofocus", required: true }
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index fd77cdbee2e186a6e7e6916e6c98c2e56bc2c543..2cadc424668a022e05099a8962f7968fe934171f 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -7,7 +7,7 @@
   .login-box
     .login-body
       - if @user.two_factor_otp_enabled?
-        = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user show-gl-field-errors' }) do |f|
+        = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user gl-show-field-errors' }) do |f|
           - resource_params = params[resource_name].presence || params
           = f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0)
           %div
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index d0bbcf3115e2cd084137f3270cd9633f5506d59d..7c68e3266e5177ffecbdf37e516d4e05af76fefb 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -1,6 +1,6 @@
 #register-pane.login-box{ role: 'tabpanel', class: 'tab-pane' }
   .login-body
-    = 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|
+    = form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f|
       .devise-errors
         = devise_error_messages!
       %div.form-group
@@ -8,7 +8,7 @@
         = 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.'
+        = f.text_field :username, class: "form-control middle", 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...
diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml
index 49b2f77111fc4c039b80a20146a8d031b67eeedb..b2f48a4e0bf252cec61cb132c0a2055d1094595d 100644
--- a/app/views/devise/unlocks/new.html.haml
+++ b/app/views/devise/unlocks/new.html.haml
@@ -1,7 +1,7 @@
 = render 'devise/shared/tab_single', tab_title: 'Resend unlock instructions'
 .login-box
   .login-body
-    = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'show-gl-field-errors' }) do |f|
+    = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post, class: 'gl-show-field-errors' }) do |f|
       .devise-errors
         = devise_error_messages!
       .form-group.append-bottom-20
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index c766370d5a0991526eace080c1cfb8a8c7a1be75..2f90c19d4b4b6a699b7d5415e5e88d5a8268fae4 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -2,13 +2,14 @@
   .panel-heading
     Group settings
   .panel-body
-    = form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f|
+    = form_for @group, html: { multipart: true, class: "form-horizontal gl-show-field-errors" }, authenticity_token: true do |f|
       = form_errors(@group)
       = render 'shared/group_form', f: f
 
       .form-group
         .col-sm-offset-2.col-sm-10
-          = image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160'
+          .image-container.s160
+            = image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160'
           %p.light
             - if @group.avatar?
               You can change your group avatar here
diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder
index b16280403253f890204ff6b74dba591caacc3c70..0cc6466d34eadaaa16348e7e9b7c9fb60ac00597 100644
--- a/app/views/groups/issues.atom.builder
+++ b/app/views/groups/issues.atom.builder
@@ -1,7 +1,7 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "#{@group.name} issues"
-  xml.link    href: issues_group_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+  xml.link    href: url_for(params), rel: "self", type: "application/atom+xml"
   xml.link    href: issues_group_url, rel: "alternate", type: "text/html"
   xml.id      issues_group_url
   xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 4434f1cbd356388cf31b0701f4253bf1802c7657..dc6c1bb69de9fdd8d2e067095b1def6b8dd98912 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,13 +1,13 @@
 - page_title "Issues"
 = content_for :meta_tags do
   - if current_user
-    = auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
+    = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@group.name} issues")
 
 .top-area
   = render 'shared/issuable/nav', type: :issues
   .nav-controls
     - if current_user
-      = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do
+      = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn' do
         = icon('rss')
         %span.icon-label
           Subscribe
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index 70783a634099e540157bb2a43ff969d7942ca19a..45325d6bc4b95f39be28c7ef1098c269cd5a64b4 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -13,7 +13,7 @@
   .other-labels
     - if @labels.present?
       %ul.content-list.manage-labels-list.js-other-labels
-        = render partial: 'shared/label', collection: @labels, as: :label
+        = render partial: 'shared/label', subject: @group, collection: @labels, as: :label
         = paginate @labels, theme: 'gitlab'
     - else
       .nothing-here-block
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 2b8bc269e645c117c15955469075b156f3406c77..d19eaa6add943ba7e0bc06e8bd7c18b35d741f73 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -5,7 +5,7 @@
   New Group
 %hr
 
-= form_for @group, html: { class: 'group-form form-horizontal' } do |f|
+= form_for @group, html: { class: 'group-form form-horizontal gl-show-field-errors' } do |f|
   = form_errors(@group)
   = render 'shared/group_form', f: f, autofocus: true
 
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index fab61f447c2c58c3e5e152cfc2a8749d91773664..275581b3af80cc3aa0285ed565fc673dc3340416 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -6,7 +6,8 @@
 
 .cover-block.groups-cover-block
   %div{ class: container_class }
-    = image_tag group_icon(@group), class: "avatar group-avatar s70 avatar-tile"
+    .image-container.s70.group-avatar
+      = image_tag group_icon(@group), class: "avatar s70 avatar-tile"
     .group-info
       .cover-title
         %h1
diff --git a/app/views/kaminari/gitlab/_gap.html.haml b/app/views/kaminari/gitlab/_gap.html.haml
index 80ca30f36e66f10675a6067016fd82e31ed7f3fc..889514c4755fa161d9d26baddd13e4568552741b 100644
--- a/app/views/kaminari/gitlab/_gap.html.haml
+++ b/app/views/kaminari/gitlab/_gap.html.haml
@@ -4,6 +4,6 @@
 -#    total_pages:   total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
-%li{class: "page"}
-  %span.page.gap
+%li
+  %span.gap
     = raw(t 'views.pagination.truncate')
diff --git a/app/views/kaminari/gitlab/_page.html.haml b/app/views/kaminari/gitlab/_page.html.haml
index 522e4d1d05fd57c70fb789f8d9177df38b055e5a..750aed8f329350297d38dd91989b6e2c1a87d322 100644
--- a/app/views/kaminari/gitlab/_page.html.haml
+++ b/app/views/kaminari/gitlab/_page.html.haml
@@ -6,5 +6,5 @@
 -#    total_pages:   total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
-%li{class: "page#{' active' if page.current?}"}
+%li{class: "page#{' active' if page.current?}#{' sibling' if page.next? || page.prev?}"}
   = link_to page, url, {remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil}
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 6922f1e153fb974ad29e5b4896b9ab50a1377c12..afd9958f0738effa3c2a7a90d9609dea5f967f33 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -26,8 +26,8 @@
                   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)
+              - if current_application_settings.sign_in_text.present?
+                = markdown_field(current_application_settings, :sign_in_text)
 
       %hr.footer-fixed
       .container.footer-container
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 7faa8bded86cc1fbd812cf5d6bf53e16dd661c36..7a9859262f7f5a8f79c7054940d52b1f6bf9e2f4 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -29,10 +29,6 @@
                 = icon('bell fw')
                 %span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
                   = todos_pending_count
-            - if current_user.can_create_project?
-              %li
-                = link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
-                  = icon('plus fw')
             - if Gitlab::Sherlock.enabled?
               %li
                 = link_to sherlock_transactions_path, title: 'Sherlock Transactions',
@@ -48,6 +44,8 @@
                     = link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username }
                   %li
                     = link_to "Profile Settings", profile_path, aria: { label: "Profile Settings" }
+                  %li
+                    = link_to "Help", help_path, aria: { label: "Help" }
                   %li.divider
                   %li
                     = link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link", aria: { label: "Sign out" }
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 3b1295dc3c04e5c7910887cd4f53d20c8019fb72..a0356feef95b401d103301660c0767e6dea8e45a 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,49 +1,38 @@
-%ul.nav.nav-sidebar
-  = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
-    = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
-      %span
-        Projects
-  = nav_link(controller: :todos) do
-    = link_to dashboard_todos_path, title: 'Todos' do
-      %span
-        Todos
-        %span.count.js-todos-count= number_with_delimiter(todos_pending_count)
-  = nav_link(path: 'dashboard#activity') do
-    = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
-      %span
-        Activity
-  - if koding_enabled?
-    = nav_link(controller: :koding) do
-      = link_to koding_path, title: 'Koding' do
+.nav-sidebar
+  .sidebar-header Across GitLab
+  %ul.nav
+    = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
+      = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
         %span
-          Koding
-  = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
-    = link_to dashboard_groups_path, title: 'Groups' do
-      %span
-        Groups
-  = nav_link(controller: 'dashboard/milestones') do
-    = link_to dashboard_milestones_path, title: 'Milestones' do
-      %span
-        Milestones
-  = nav_link(path: 'dashboard#issues') do
-    = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
-      %span
-        Issues
-        %span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
-  = nav_link(path: 'dashboard#merge_requests') do
-    = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
-      %span
-        Merge Requests
-        %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
-  = nav_link(controller: 'dashboard/snippets') do
-    = link_to dashboard_snippets_path, title: 'Snippets' do
-      %span
-        Snippets
-  = nav_link(controller: :help) do
-    = link_to help_path, title: 'Help' do
-      %span
-        Help
-  = nav_link(html_options: {class: profile_tab_class}) do
-    = link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
-      %span
-        Profile Settings
+          Projects
+    = nav_link(path: 'dashboard#activity') do
+      = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
+        %span
+          Activity
+    - if koding_enabled?
+      = nav_link(controller: :koding) do
+        = link_to koding_path, title: 'Koding' do
+          %span
+            Koding
+    = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
+      = link_to dashboard_groups_path, title: 'Groups' do
+        %span
+          Groups
+    = nav_link(controller: 'dashboard/milestones') do
+      = link_to dashboard_milestones_path, title: 'Milestones' do
+        %span
+          Milestones
+    = nav_link(path: 'dashboard#issues') do
+      = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
+        %span
+          Issues
+          %span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
+    = nav_link(path: 'dashboard#merge_requests') do
+      = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
+        %span
+          Merge Requests
+          %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
+    = nav_link(controller: 'dashboard/snippets') do
+      = link_to dashboard_snippets_path, title: 'Snippets' do
+        %span
+          Snippets
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index d3987fc9c4f460fa446a87f7017fd67e3c65d7a9..e67b66d1fffb4d5658702b2e93a41295eede2c49 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,7 +1,8 @@
 - empty_repo = @project.empty_repo?
 .project-home-panel.text-center{ class: ("empty-project" if empty_repo) }
   %div{ class: container_class }
-    = project_icon(@project, alt: @project.name, class: 'project-avatar avatar s70 avatar-tile')
+    .image-container.s70.project-avatar
+      = project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile')
     %h1.project-title
       = @project.name
       %span.visibility-icon.has-tooltip{data: { container: 'body' }, title: visibility_icon_description(@project)}
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index d4f59764a7066efa7e0960d9c2a6b2fab6ecb57f..4a6aa92e3f3db04685091e0882c416a59e2a3ac2 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -14,13 +14,13 @@
       = text_field_tag 'file_name', params[:file_name], placeholder: "File name",
         required: true, class: 'form-control new-file-name'
 
-    .pull-right
+    .pull-right.file-buttons
       .license-selector.js-license-selector-wrap.hidden
-        = dropdown_tag("Choose a License template", options: { toggle_class: 'js-license-selector', title: "Choose a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } )
+        = dropdown_tag("Choose a License template", options: { toggle_class: 'btn js-license-selector', title: "Choose a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } )
       .gitignore-selector.js-gitignore-selector-wrap.hidden
-        = dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } )
+        = dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'btn js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } )
       .gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.hidden
-        = dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } )
+        = dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'btn js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } )
       = button_tag class: 'soft-wrap-toggle btn', type: 'button' do
         %span.no-wrap
           = custom_icon('icon_no_wrap')
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index b1f50eb5f34ba50d771bbab69945e209137915c6..57a27ec904e4148b9e8159088340990af9ae7e35 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -26,6 +26,6 @@
 
 
 :javascript
-  disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
+  gl.utils.disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
   new BlobFileDropzone($('.js-upload-blob-form'), '#{method}');
   new NewCommitForm($('.js-upload-blob-form'))
diff --git a/app/views/projects/boards/components/_board.html.haml b/app/views/projects/boards/components/_board.html.haml
index ba1502c97b64ab7926fe27e237d6ba3e92fa552b..f7071051efca868b014800ff912f63e53522f8c1 100644
--- a/app/views/projects/boards/components/_board.html.haml
+++ b/app/views/projects/boards/components/_board.html.haml
@@ -11,7 +11,9 @@
     .board-inner
       %header.board-header{ ":class" => "{ 'has-border': list.label }", ":style" => "{ borderTopColor: (list.label ? list.label.color : null) }" }
         %h3.board-title.js-board-handle{ ":class" => "{ 'user-can-drag': (!disabled && !list.preset) }" }
-          {{ list.title }}
+          %span.has-tooltip{ ":title" => "(list.label ? list.label.description : '')",
+            data: { container: "body", placement: "bottom" } }
+            {{ list.title }}
           .board-issue-count-holder.pull-right.clearfix{ "v-if" => "list.type !== 'blank'" }
             %span.board-issue-count.pull-left{ ":class" => "{ 'has-btn': list.type !== 'done' }" }
               {{ list.issuesSize }}
diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml
index d54c76ff9c813d22f01a68ceba0cc3b5b9588efa..de607772df61e92101f56b6259988c6499a64eb9 100644
--- a/app/views/projects/branches/_commit.html.haml
+++ b/app/views/projects/branches/_commit.html.haml
@@ -1,4 +1,6 @@
 .branch-commit
+  .icon-container.commit-icon
+    = custom_icon("icon_commit")
   = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-id monospace"
   &middot;
   %span.str-truncated
diff --git a/app/views/projects/builds/_header.html.haml b/app/views/projects/builds/_header.html.haml
index 51b5bd9db427d53a5c0c78d76de0edfbcb4370e0..3f2ce7377fdee1b3786159dc71e438f7e8faf0df 100644
--- a/app/views/projects/builds/_header.html.haml
+++ b/app/views/projects/builds/_header.html.haml
@@ -1,16 +1,19 @@
 .content-block.build-header
-  = ci_status_with_icon(@build.status)
-  Build
-  %strong ##{@build.id}
-  for commit
-  = link_to ci_status_path(@build.pipeline) do
-    %strong= @build.pipeline.short_sha
-  from
-  = link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
-    %code
-      = @build.ref
-  - if @build.user
-    = render "user"
-  = time_ago_with_tooltip(@build.created_at)
+  .header-content
+    = ci_status_with_icon(@build.status)
+    Build
+    %strong ##{@build.id}
+    for commit
+    = link_to ci_status_path(@build.pipeline) do
+      %strong= @build.pipeline.short_sha
+    from
+    = link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
+      %code
+        = @build.ref
+    - if @build.user
+      = render "user"
+    = time_ago_with_tooltip(@build.created_at)
+  - if can?(current_user, :update_build, @build) && @build.retryable?
+    = link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted pull-right', method: :post
   %button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
     = icon('angle-double-left')
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
index b1053028279c95ef7b57feccfcd681f026703e7c..28f519f11b256d93220b3ea24314322ee85429bd 100644
--- a/app/views/projects/builds/_sidebar.html.haml
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -44,7 +44,7 @@
       .title
         Build details
         - if can?(current_user, :update_build, @build) && @build.retryable?
-          = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post
+          = link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post
       - if @build.merge_request
         %p.build-detail-row
           %span.build-light-text Merge Request:
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 29d549a60f53aa3b2950b6db932c7b72eab83464..27da86b9efe614dc81e70b0b9a82c932a05ee092 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -5,10 +5,10 @@
         = custom_icon('icon_fork')
         %span Fork
     - else
-      = link_to new_namespace_project_fork_path(@project.namespace, @project), title: 'Fork project', class: 'btn has-tooltip' do
+      = link_to new_namespace_project_fork_path(@project.namespace, @project), title: 'Fork project', class: 'btn' do
         = custom_icon('icon_fork')
         %span Fork
     %div.count-with-arrow
       %span.arrow
-      = link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks', class: 'count has-tooltip' do
+      = link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks', class: 'count' do
         = @project.forks_count
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 311583037e5626505e8dbadcf89d1f5a35873fd2..12d351017705883037bd44298d53a7e3db6b722a 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,5 +1,5 @@
 - if current_user
-  = link_to toggle_star_namespace_project_path(@project.namespace, @project), { class: 'btn star-btn toggle-star has-tooltip', method: :post, remote: true, title: current_user.starred?(@project) ? 'Unstar project' : 'Star project' } do
+  = link_to toggle_star_namespace_project_path(@project.namespace, @project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do
     - if current_user.starred?(@project)
       = icon('star')
       %span.starred Unstar
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index 840f468dc05c118ced8d8172d0eeeecf654c281a..1f748d73d061c8a7d9b2d8f86ee984758446b9cd 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -41,7 +41,7 @@
       - else
         Cant find HEAD commit for this branch
 
-  - stages_status = pipeline.statuses.relevant.latest.stages_status
+  - stages_status = pipeline.statuses.latest.stages_status
   %td.stage-cell
     - stages.each do |stage|
       - status = stages_status[stage]
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 30473d14b9b6a756d794473e7b9599f1cd15e253..a54229666179acb8d15d8592dd723e3afb58c138 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -102,7 +102,7 @@
                   = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
 
         - if Gitlab.config.registry.enabled
-          .form-group
+          .form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) }
             .checkbox
               = f.label :container_registry_enabled do
                 = f.check_box :container_registry_enabled
@@ -118,7 +118,8 @@
             Project avatar
           .form-group
             - if @project.avatar?
-              = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
+              .image-container.s160
+                = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
             %p.light
               - if @project.avatar_in_git
                 Project avatar in repository: #{ @project.avatar_in_git }
@@ -180,6 +181,7 @@
           %ul
             %li Build traces and artifacts
             %li LFS objects
+            %li Container registry images
   %hr
   - if can? current_user, :archive_project, @project
     .row.prepend-top-default
@@ -288,4 +290,4 @@
       Saving project.
     %p Please wait a moment, this page will automatically refresh when ready.
 
-= render 'shared/confirm_modal', phrase: @project.path
\ No newline at end of file
+= render 'shared/confirm_modal', phrase: @project.path
diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder
index 36957560de0cf6466c0aea564ee3050d417d8e4d..a0df0db77c5cecc6e5fbc0c6488df3724e08b390 100644
--- a/app/views/projects/issues/index.atom.builder
+++ b/app/views/projects/issues/index.atom.builder
@@ -1,7 +1,7 @@
 xml.instruct!
 xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
   xml.title   "#{@project.name} issues"
-  xml.link    href: namespace_project_issues_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
+  xml.link    href: url_for(params), rel: "self", type: "application/atom+xml"
   xml.link    href: namespace_project_issues_url(@project.namespace, @project), rel: "alternate", type: "text/html"
   xml.id      namespace_project_issues_url(@project.namespace, @project)
   xml.updated @issues.first.created_at.xmlschema if @issues.reorder(nil).any?
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index cc57cfdb342340cfc9bdadf4d5d26f01b9a8b17a..c493ff3585b468b31d2a7d0e20375e4f56372b5a 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -8,7 +8,7 @@
 
 = content_for :meta_tags do
   - if current_user
-    = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
+    = auto_discovery_link_tag(:atom, url_for(params.merge(format: :atom, private_token: current_user.private_token)), title: "#{@project.name} issues")
 
 %div{ class: (container_class) }
   - if @project.issues.any?
@@ -16,7 +16,7 @@
       = render 'shared/issuable/nav', type: :issues
       .nav-controls
         - if current_user
-          = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
+          = link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn append-right-10' do
             = icon('rss')
             %span.icon-label
               Subscribe
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index f135bf6f6b45636066383331b9e1cd01563870db..05a8475dcd68c22cb6364476874de91f701d940d 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -22,14 +22,14 @@
         %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) }
           %p.empty-message{ class: ('hidden' unless @prioritized_labels.empty?) } No prioritized labels yet
           - if @prioritized_labels.present?
-            = render partial: 'shared/label', collection: @prioritized_labels, as: :label
+            = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label
 
     .other-labels
       - if can?(current_user, :admin_label, @project)
         %h5{ class: ('hide' if hide) } Other Labels
       %ul.content-list.manage-labels-list.js-other-labels
         - if @labels.present?
-          = render partial: 'shared/label', collection: @labels, as: :label
+          = render partial: 'shared/label', subject: @project, collection: @labels, as: :label
         = paginate @labels, theme: 'gitlab'
       - if @labels.blank?
         .nothing-here-block
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index ba16c641462aaebb5b9019c6fc0c0cbce9f8d816..d2570598501c9ff031eba0e7816b7b4fe4cfe9b9 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -12,7 +12,7 @@
 = render 'projects/last_push'
 = render "home_panel"
 
-- if @project.feature_available?(:repository, current_user)
+- if current_user && can?(current_user, :download_code, @project)
   %nav.project-stats{ class: container_class }
     %ul.nav
       %li
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index 0f7d629ab98fb9bd42a9aa6c5ee699b0dfb921e3..21e378b87355a60518d3f5aa96af10977f029eda 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -37,5 +37,5 @@
 :javascript
   // Load last commit log for each file in tree
   $('#tree-slider').waitForImages(function() {
-    ajaxGet("#{escape_javascript(@logs_path)}");
+    gl.utils.ajaxGet("#{escape_javascript(@logs_path)}");
   });
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 6624d5cb427e834dc435305a59c6a82063bba513..4e41a15d9f4e8cd96d2673e0766ce03454ae9713 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -33,7 +33,12 @@
   .form-actions
     - if @page && @page.persisted?
       = f.submit 'Save changes', class: "btn-save btn"
-      = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-cancel"
+      .pull-right
+        - if can?(current_user, :admin_wiki, @project)
+          = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-danger btn-grouped" do
+            Delete
+        = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-cancel btn-grouped"
     - else
       = f.submit 'Create page', class: "btn-create btn"
-      = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel"
+      .pull-right
+        = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel"
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 4ea75dbbf0cdf4d0b738b7cdf62ac8d6b64214f4..763c2fea39b4eea90d5f55e65783c395a073c3db 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -7,6 +7,3 @@
   - if can?(current_user, :create_wiki, @project)
     = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn" do
       Edit
-  - if can?(current_user, :admin_wiki, @project)
-    = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-remove" do
-      Delete
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 233538bb488cff19f8c1606c02ea9cb36c8dacfe..679d6018befedc3f46a0aacd24d5ee40d7cfe8a0 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -19,7 +19,5 @@
         - if can?(current_user, :create_wiki, @project)
           = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
             New Page
-      = render 'main_links'
-
 
   = render 'form'
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index 3480800369a13c1930eb9336f5400a235178fd39..c367ae336db288021750db2f10328cd50887b845 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,6 +1,9 @@
 %ul.nav-links.event-filter.scrolling-tabs
   = event_filter_link EventFilter.all, 'All'
-  = event_filter_link EventFilter.push, 'Push events'
-  = event_filter_link EventFilter.merged, 'Merge events'
-  = event_filter_link EventFilter.comments, 'Comments'
+  - if event_filter_visible(:repository)
+    = event_filter_link EventFilter.push, 'Push events'
+  - if event_filter_visible(:merge_requests)
+    = event_filter_link EventFilter.merged, 'Merge events'
+  - if event_filter_visible(:issues)
+    = event_filter_link EventFilter.comments, 'Comments'
   = event_filter_link EventFilter.team, 'Team'
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index 67072b9fc2abb513da29f06315bab8b921ec4edd..ba25e09d638abd54bf1cc2ddced24148d9ad852d 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -9,11 +9,13 @@
   = f.label :path, class: 'control-label' do
     Group path
   .col-sm-10
-    .input-group
+    .input-group.gl-field-error-anchor
       .input-group-addon
         = root_url
       = f.text_field :path, placeholder: 'open-source', class: 'form-control',
-          autofocus: local_assigns[:autofocus] || false
+        autofocus: local_assigns[:autofocus] || false,  pattern: "[a-zA-Z0-9-_]+",
+        required: true, title: 'Please choose a group name with no special characters.'
+
     - if @group.persisted?
       .alert.alert-warning.prepend-top-10
         %ul
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index 40c8d2af226050cbc01b46ca1f465fc52440b610..6ccdef0df465aa3cf0d2bc07b2b06f19c01c91bc 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -1,6 +1,7 @@
 - label_css_id = dom_id(label)
-- open_issues_count = label.open_issues_count(current_user, @project)
-- open_merge_requests_count = label.open_merge_requests_count(current_user, @project)
+- open_issues_count = label.open_issues_count(current_user)
+- open_merge_requests_count = label.open_merge_requests_count(current_user)
+- subject = local_assigns[:subject]
 
 %li{id: label_css_id, data: { id: label.id } }
   = render "shared/label_row", label: label
@@ -12,10 +13,10 @@
     .dropdown-menu.dropdown-menu-align-right
       %ul
         %li
-          = link_to_label(label, subject: @project, type: :merge_request) do
+          = link_to_label(label, subject: subject, type: :merge_request) do
             = pluralize open_merge_requests_count, 'merge request'
         %li
-          = link_to_label(label, subject: @project) do
+          = link_to_label(label, subject: subject) do
             = pluralize open_issues_count, 'open issue'
         - if current_user
           %li.label-subscription{ data: toggle_subscription_data(label) }
@@ -28,9 +29,9 @@
             = link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, remote: true, data: {confirm: 'Remove this label? Are you sure?'}
 
   .pull-right.hidden-xs.hidden-sm.hidden-md
-    = link_to_label(label, subject: @project, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
+    = link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
       = pluralize open_merge_requests_count, 'merge request'
-    = link_to_label(label, subject: @project, css_class: 'btn btn-transparent btn-action') do
+    = link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do
       = pluralize open_issues_count, 'open issue'
 
     - if current_user
diff --git a/app/views/shared/empty_states/_todos_all_done.svg b/app/views/shared/empty_states/_todos_all_done.svg
new file mode 100644
index 0000000000000000000000000000000000000000..94b5c2e0ea0928b6cb0c171b0147e1deb6ac1690
--- /dev/null
+++ b/app/views/shared/empty_states/_todos_all_done.svg
@@ -0,0 +1 @@
+<svg viewBox="0 0 293 216"><g fill="none" fill-rule="evenodd"><g transform="rotate(-5 211.388 -693.89)"><rect width="163.6" height="200" x=".2" fill="#FFF" stroke="#EEE" stroke-width="3" stroke-linecap="round" stroke-dasharray="6 9" rx="6"/><g transform="translate(24 38)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="76" height="3" x="40" y="11" fill="#6B4FBB" opacity=".5" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#6B4FBB" opacity=".5" rx="1.5"/></g><g transform="translate(24 83)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="76" height="3" x="40" y="11" fill="#B5A7DD" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#B5A7DD" rx="1.5"/></g><g transform="translate(24 130)"><path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/><path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/><rect width="76" height="3" x="40" y="11" fill="#B5A7DD" rx="1.5"/><rect width="43" height="3" x="40" y="21" fill="#B5A7DD" rx="1.5"/></g></g><path fill="#FFCE29" d="M30 11l-1.8 4-2-4-4-1.8 4-2 2-4 2 4 4 2M286 60l-2.7 6.3-3-6-6-3 6-3 3-6 2.8 6.2 6.6 2.8M263 97l-2 4-2-4-4-2 4-2 2-4 2 4 4 2M12 85l-2.7 6.3-3-6-6-3 6-3 3-6 2.8 6.2 6.6 2.8"/></g></svg>
diff --git a/app/views/shared/empty_states/_todos_empty.svg b/app/views/shared/empty_states/_todos_empty.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b1e661268fb12f8db313f9f44670dca94567daf0
--- /dev/null
+++ b/app/views/shared/empty_states/_todos_empty.svg
@@ -0,0 +1,110 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 284 337" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <defs>
+    <rect id="a" width="180" height="220" x="66.2" y="74.4" rx="6"/>
+    <mask id="l" width="180" height="220" x="0" y="0" fill="#fff">
+      <use xlink:href="#a"/>
+    </mask>
+    <rect id="b" width="180" height="220" rx="6"/>
+    <mask id="m" width="180" height="220" x="0" y="0" fill="#fff">
+      <use xlink:href="#b"/>
+    </mask>
+    <rect id="c" width="28" height="28" rx="4"/>
+    <mask id="n" width="28" height="28" x="0" y="0" fill="#fff">
+      <use xlink:href="#c"/>
+    </mask>
+    <rect id="d" width="28" height="28" rx="4"/>
+    <mask id="o" width="28" height="28" x="0" y="0" fill="#fff">
+      <use xlink:href="#d"/>
+    </mask>
+    <circle id="e" cx="21.5" cy="21.5" r="21.5"/>
+    <mask id="p" width="43" height="43" x="0" y="0" fill="#fff">
+      <use xlink:href="#e"/>
+    </mask>
+    <circle id="f" cx="26.5" cy="26.5" r="26.5"/>
+    <mask id="q" width="53" height="53" x="0" y="0" fill="#fff">
+      <use xlink:href="#f"/>
+    </mask>
+    <circle id="g" cx="9.5" cy="4.5" r="4.5"/>
+    <mask id="r" width="13" height="13" x="-2" y="-2">
+      <path fill="#fff" d="M3-2h13v13H3z"/>
+      <use xlink:href="#g"/>
+    </mask>
+    <circle id="h" cx="26.5" cy="26.5" r="26.5"/>
+    <mask id="s" width="53" height="53" x="0" y="0" fill="#fff">
+      <use xlink:href="#h"/>
+    </mask>
+    <circle id="i" cx="21.5" cy="21.5" r="21.5"/>
+    <mask id="t" width="43" height="43" x="0" y="0" fill="#fff">
+      <use xlink:href="#i"/>
+    </mask>
+    <path id="j" d="M18 38h15c10.5 0 19-8.5 19-19S43.5 0 33 0H19C8.5 0 0 8.5 0 19c0 6.3 3 12 7.8 15.3l5.2 9c.6 1 1.4 1 2 0l3-5.3z"/>
+    <mask id="u" width="52" height="44" x="0" y="0" fill="#fff">
+      <use xlink:href="#j"/>
+    </mask>
+    <circle id="k" cx="18.5" cy="18.5" r="18.5"/>
+    <mask id="v" width="37" height="37" x="0" y="0" fill="#fff">
+      <use xlink:href="#k"/>
+    </mask>
+  </defs>
+  <g fill="none" fill-rule="evenodd" transform="translate(-6 -4)">
+    <use stroke="#EEE" stroke-width="6" mask="url(#l)" transform="rotate(-5 156.245 184.425)" xlink:href="#a"/>
+    <g transform="rotate(5 -707.333 618.042)">
+      <use fill="#FFF" stroke="#EEE" stroke-width="6" mask="url(#m)" xlink:href="#b"/>
+      <g transform="translate(29 24)">
+        <path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/>
+        <path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/>
+        <rect width="86" height="3" x="40" y="11" fill="#6B4FBB" opacity=".5" rx="1.5"/>
+        <rect width="43" height="3" x="40" y="21" fill="#6B4FBB" opacity=".5" rx="1.5"/>
+      </g>
+      <g transform="translate(29 69)">
+        <path fill="#FC6D26" d="M18.2 14l-4-3.8c-.4-.6-1.4-.6-2 0-.6.6-.6 1.5 0 2l5 5c.3.4.6.5 1 .5s.8 0 1-.4L28 8.8c.6-.6.6-1.5 0-2-.6-.7-1.6-.7-2 0L18 14z"/>
+        <path stroke="#6B4FBB" stroke-width="3" d="M27 23.3V27c0 2.3-1.7 4-4 4H4c-2.3 0-4-1.7-4-4V8c0-2.3 1.7-4 4-4h3.8" stroke-linecap="round"/>
+        <rect width="86" height="3" x="40" y="11" fill="#B5A7DD" rx="1.5"/>
+        <rect width="43" height="3" x="40" y="21" fill="#B5A7DD" rx="1.5"/>
+      </g>
+      <g transform="translate(28 160)">
+        <use stroke="#E5E5E5" stroke-width="6" mask="url(#n)" opacity=".7" xlink:href="#c"/>
+        <rect width="26" height="3" x="41" y="7" fill="#ECECEC" rx="1.5"/>
+        <rect width="43" height="3" x="41" y="17" fill="#ECECEC" rx="1.5"/>
+      </g>
+      <g transform="translate(28 116)">
+        <use stroke="#E5E5E5" stroke-width="6" mask="url(#o)" xlink:href="#d"/>
+        <rect width="86" height="3" x="41" y="7" fill="#E5E5E5" rx="1.5"/>
+        <rect width="43" height="3" x="41" y="17" fill="#E5E5E5" rx="1.5"/>
+      </g>
+    </g>
+    <g transform="rotate(-15 601.917 -782.362)">
+      <use fill="#FFF" stroke="#B5A7DD" stroke-width="6" mask="url(#p)" xlink:href="#e"/>
+      <text fill="#6B4FBB" font-family="SourceSansPro-Black, Source Sans Pro" font-size="20" font-weight="700" letter-spacing="-.1">
+        <tspan x="12" y="27">@</tspan>
+      </text>
+    </g>
+    <g transform="rotate(15 -686.59 1035.907)">
+      <use fill="#FFF" stroke="#FDE5D8" stroke-width="6" mask="url(#q)" xlink:href="#f"/>
+      <path fill="#FC6D26" d="M26.5 38.2c3.3 0 9.5-2.5 9.5-9.6 0-7-2.4-6.6-9.5-6.6-7 0-9.5-.4-9.5 6.6s6.2 9.6 9.5 9.6z"/>
+      <g transform="translate(17 14)">
+        <use fill="#FC6D26" xlink:href="#g"/>
+        <use stroke="#FFF" stroke-width="4" mask="url(#r)" xlink:href="#g"/>
+      </g>
+    </g>
+    <g transform="rotate(15 -85.125 65.185)">
+      <use fill="#FFF" stroke="#B5A7DD" stroke-width="6" mask="url(#s)" xlink:href="#h"/>
+      <path fill="#6B4FBB" d="M24 18.5c0-1.4 1-2.5 2.5-2.5 1.4 0 2.5 1 2.5 2.5v9c0 1.4-1 2.5-2.5 2.5-1.4 0-2.5-1-2.5-2.5v-9zM26.5 37c1.4 0 2.5-1 2.5-2.5 0-1.4-1-2.5-2.5-2.5-1.4 0-2.5 1-2.5 2.5 0 1.4 1 2.5 2.5 2.5z"/>
+    </g>
+    <g transform="rotate(-15 716.492 78.873)">
+      <use fill="#FFF" stroke="#FDE5D8" stroke-width="6" mask="url(#t)" xlink:href="#i"/>
+      <path fill="#FC6D26" d="M20 23v-3h3v3h-3zm0 3v1.5c0 .8-.7 1.5-1.5 1.5s-1.5-.7-1.5-1.5V26h-2.5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5H17v-3h-1.5c-.8 0-1.5-.7-1.5-1.5s.7-1.5 1.5-1.5H17v-2.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V17h3v-1.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5V17h2.5c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5H26v3h1.5c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5H26v2.5c0 .8-.7 1.5-1.5 1.5s-1.5-.7-1.5-1.5V26h-3z"/>
+    </g>
+    <g transform="rotate(-15 129.114 -585.74)">
+      <use stroke="#FDE5D8" stroke-width="6" mask="url(#u)" xlink:href="#j"/>
+      <circle cx="16" cy="20" r="2" fill="#FC6D26"/>
+      <circle cx="27" cy="20" r="2" fill="#FC6D26"/>
+      <circle cx="38" cy="20" r="2" fill="#FC6D26"/>
+    </g>
+    <g transform="rotate(-15 1254.8 -458.986)">
+      <use stroke="#FDE5D8" stroke-width="6" mask="url(#v)" xlink:href="#k"/>
+      <path fill="#FC6D26" d="M10.6 19l2-2c.5-.5.5-1 0-1.5-.3-.4-1-.4-1.3 0l-2.8 2.8c-.2.2-.3.4-.3.7 0 .3 0 .5.3.7l2.8 2.8c.4.4 1 .4 1.4 0 .4-.4.4-1 0-1.4l-2-2zm14.8 0l-2-2c-.5-.5-.5-1 0-1.5.3-.4 1-.4 1.3 0l2.8 2.8c.2.2.3.4.3.7 0 .3 0 .5-.3.7l-2.8 2.8c-.4.4-1 .4-1.4 0-.4-.4-.4-1 0-1.4l2-2z"/>
+      <rect width="2" height="7" x="17" y="15.1" fill="#FC6D26" opacity=".5" transform="rotate(15 18.002 18.64)" rx="1"/>
+    </g>
+  </g>
+</svg>
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index dc4ee3074d20e77c36263871532a5448ab5bcfbb..562291a61df41769f9a454e937b5e247430b9972 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -24,7 +24,8 @@
     %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
       = visibility_level_icon(group.visibility_level, fw: false)
 
-  = image_tag group_icon(group), class: "avatar s40 hidden-xs"
+  .image-container.s40
+    = image_tag group_icon(group), class: "avatar s40 hidden-xs"
   .title
     = link_to group, class: 'group-name' do
       = group.name
diff --git a/app/views/shared/icons/_icon_status_cancel.svg b/app/views/shared/icons/_icon_status_canceled.svg
similarity index 79%
rename from app/views/shared/icons/_icon_status_cancel.svg
rename to app/views/shared/icons/_icon_status_canceled.svg
index fd1ebbcbabda8928110260ab8877f317f6a70c07..1b2d0891244abf60dc1859e44b19a952ffd54806 100644
--- a/app/views/shared/icons/_icon_status_cancel.svg
+++ b/app/views/shared/icons/_icon_status_canceled.svg
@@ -1,4 +1,4 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14">
+<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" class="ci-status-icon-canceled" viewBox="0 0 14 14">
   <g fill="#5C5C5C" fill-rule="evenodd">
     <path d="M12.5,7 C12.5,3.96243388 10.0375661,1.5 7,1.5 C3.96243388,1.5 1.5,3.96243388 1.5,7 C1.5,10.0375661 3.96243388,12.5 7,12.5 C10.0375661,12.5 12.5,10.0375661 12.5,7 Z M0,7 C0,3.13400675 3.13400675,0 7,0 C10.8659932,0 14,3.13400675 14,7 C14,10.8659932 10.8659932,14 7,14 C3.13400675,14 0,10.8659932 0,7 Z"/>
     <rect width="8" height="2" x="3" y="6" transform="rotate(45 7 7)" rx=".5"/>
diff --git a/app/views/shared/icons/_icon_status_created.svg b/app/views/shared/icons/_icon_status_created.svg
index 1f5c3b51b0386aaf4fecf79893bf38714b37169b..dca5d2897674bb947bd247aca80c1af9e001a646 100644
--- a/app/views/shared/icons/_icon_status_created.svg
+++ b/app/views/shared/icons/_icon_status_created.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" enable-background="new 0 0 14 14"><path d="M12.5,7 C12.5,4 10,1.5 7,1.5 C4,1.5 1.5,4 1.5,7 C1.5,10 4,12.5 7,12.5 C10,12.5 12.5,10 12.5,7 L12.5,7 Z M0,7 C0,3.1 3.1,0 7,0 C10.9,0 14,3.1 14,7 C14,10.9 10.9,14 7,14 C3.1,14 0,10.9 0,7 L0,7 Z" /><circle cx="7" cy="7" r="3.25"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" class="ci-status-icon-created" viewBox="0 0 14 14" enable-background="new 0 0 14 14"><path d="M12.5,7 C12.5,4 10,1.5 7,1.5 C4,1.5 1.5,4 1.5,7 C1.5,10 4,12.5 7,12.5 C10,12.5 12.5,10 12.5,7 L12.5,7 Z M0,7 C0,3.1 3.1,0 7,0 C10.9,0 14,3.1 14,7 C14,10.9 10.9,14 7,14 C3.1,14 0,10.9 0,7 L0,7 Z" /><circle cx="7" cy="7" r="3.25"/></svg>
diff --git a/app/views/shared/icons/_icon_status_skipped.svg b/app/views/shared/icons/_icon_status_skipped.svg
new file mode 100644
index 0000000000000000000000000000000000000000..014ca86b61b014e9e7a11d9db4492dd9bb215617
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_skipped.svg
@@ -0,0 +1 @@
+<svg width="20" height="20" class="ci-status-icon-skipped" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><title>Group Copy 31</title><g fill="#5C5C5C" fill-rule="evenodd"><path d="M10 17.857c4.286 0 7.857-3.571 7.857-7.857S14.286 2.143 10 2.143 2.143 5.714 2.143 10 5.714 17.857 10 17.857M10 0c5.571 0 10 4.429 10 10s-4.429 10-10 10S0 15.571 0 10 4.429 0 10 0"/><path d="M10.986 11l-1.293 1.293a1 1 0 0 0 1.414 1.414l2.644-2.644a1.505 1.505 0 0 0 0-2.126l-2.644-2.644a1 1 0 0 0-1.414 1.414L10.986 9H6.4a1 1 0 0 0 0 2h4.586z"/></g></svg>
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index d410755cad11e950fa3b81bf3781700c2bfc786d..0ace6be8f4e206c37376abbe5e966febf8e10d3c 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -142,6 +142,7 @@
       .col-sm-10.col-sm-offset-2
         .checkbox
           = label_tag 'merge_request[force_remove_source_branch]' do
+            = hidden_field_tag 'merge_request[force_remove_source_branch]', '0'
             = check_box_tag 'merge_request[force_remove_source_branch]', '1', @merge_request.force_remove_source_branch?
             Remove source branch when merge request is accepted.
 
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index 22b5a6aa11bebb39a9f273de03656dc93564fea7..1d778bc88dec457a043cf6c53e190707f1ee2bd6 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -22,7 +22,7 @@
   %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
     %span.dropdown-toggle-text{ class: ("is-default" if selected.nil? || selected.empty?) }
       = multi_label_name(selected, "Labels")
-    = icon('chevron-down')
+    = icon('caret-down')
   .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
     = 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)
diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml
index f27a9002ec2d1f6e89132690395e223a5bdc53c8..40fe53e6a8daca6facd60ed80bd670ebd5d9ed60 100644
--- a/app/views/shared/issuable/_milestone_dropdown.html.haml
+++ b/app/views/shared/issuable/_milestone_dropdown.html.haml
@@ -1,10 +1,10 @@
 - project = @target_project || @project
 - extra_class = extra_class || ''
 - show_menu_above = show_menu_above || false
-- selected_text = selected.try(:title)
+- selected_text = selected.try(:title) || params[:milestone_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)
+  = hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id)
 = 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
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index e8668048703311a56dad698bc8167c2edf90511b..3d2122a159ced6b3b0be8aa33fbf79bccb539b72 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -32,10 +32,11 @@
       = link_to project_path(project), class: dom_class(project) do
         - if avatar
           .dash-project-avatar
-            - if use_creator_avatar
-              = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
-            - else
-              = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+            .image-container.s40
+              - if use_creator_avatar
+                = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
+              - else
+                = project_icon(project, alt: '', class: 'avatar project-avatar s40')
         %span.project-full-name
           %span.namespace-name
             - if project.namespace && !skip_namespace
diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml
index f360fbb3d5d94159871f64e52cf0b9dae86a4886..78f253f90233a0d3f7f9a96d9e53b7274f45308e 100644
--- a/app/views/users/_groups.html.haml
+++ b/app/views/users/_groups.html.haml
@@ -1,4 +1,5 @@
 .clearfix
   - groups.each do |group|
     = link_to group, class: 'profile-groups-avatars inline', title: group.name do
-      = image_tag group_icon(group), class: 'avatar group-avatar s40'
+      .image-container.s40
+        = image_tag group_icon(group), class: 'avatar group-avatar s40'
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index eee0ca12af9e87a112545c1ded7f2d2460c09397..2fff6b0105d1949c868517da72dbc6b8656545ea 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -16,7 +16,7 @@ class PostReceive
     post_received = Gitlab::GitPostReceive.new(repo_path, identifier, changes)
 
     if post_received.project.nil?
-      log("Triggered hook for non-existing project with full path \"#{repo_path} \"")
+      log("Triggered hook for non-existing project with full path \"#{repo_path}\"")
       return false
     end
 
@@ -25,7 +25,7 @@ class PostReceive
     elsif post_received.regular_project?
       process_project_changes(post_received)
     else
-      log("Triggered hook for unidentifiable repository type with full path \"#{repo_path} \"")
+      log("Triggered hook for unidentifiable repository type with full path \"#{repo_path}\"")
       false
     end
   end
@@ -37,7 +37,7 @@ class PostReceive
       @user ||= post_received.identify(newrev)
 
       unless @user
-        log("Triggered hook for non-existing user \"#{post_received.identifier} \"")
+        log("Triggered hook for non-existing user \"#{post_received.identifier}\"")
         return false
       end
 
diff --git a/bin/changelog b/bin/changelog
new file mode 100755
index 0000000000000000000000000000000000000000..b6586ebb6aae010adb486f269065b584a837d4ff
--- /dev/null
+++ b/bin/changelog
@@ -0,0 +1,170 @@
+#!/usr/bin/env ruby
+#
+# Generate a changelog entry file in the correct location.
+#
+# Automatically stages the file and amends the previous commit if the `--amend`
+# argument is used.
+
+require 'optparse'
+require 'yaml'
+
+Options = Struct.new(
+  :amend,
+  :author,
+  :dry_run,
+  :force,
+  :merge_request,
+  :title
+)
+
+class ChangelogOptionParser
+  def self.parse(argv)
+    options = Options.new
+
+    parser = OptionParser.new do |opts|
+      opts.banner = "Usage: #{__FILE__} [options] [title]\n\n"
+
+      # Note: We do not provide a shorthand for this in order to match the `git
+      # commit` interface
+      opts.on('--amend', 'Amend the previous commit') do |value|
+        options.amend = value
+      end
+
+      opts.on('-f', '--force', 'Overwrite an existing entry') do |value|
+        options.force = value
+      end
+
+      opts.on('-m', '--merge-request [integer]', Integer, 'Merge Request ID') do |value|
+        options.merge_request = value
+      end
+
+      opts.on('-n', '--dry-run', "Don't actually write anything, just print") do |value|
+        options.dry_run = value
+      end
+
+      opts.on('-u', '--git-username', 'Use Git user.name configuration as the author') do |value|
+        options.author = git_user_name if value
+      end
+
+      opts.on('-h', '--help', 'Print help message') do
+        $stdout.puts opts
+        exit
+      end
+    end
+
+    parser.parse!(argv)
+
+    # Title is everything that remains, but let's clean it up a bit
+    options.title = argv.join(' ').strip.squeeze(' ').tr("\r\n", '')
+
+    options
+  end
+
+  def self.git_user_name
+    %x{git config user.name}.strip
+  end
+end
+
+class ChangelogEntry
+  attr_reader :options
+
+  def initialize(options)
+    @options = options
+
+    assert_feature_branch!
+    assert_new_file!
+    assert_title!
+
+    $stdout.puts "\e[32mcreate\e[0m #{file_path}"
+    $stdout.puts contents
+
+    unless options.dry_run
+      write
+      amend_commit if options.amend
+    end
+  end
+
+  def contents
+    YAML.dump(
+      'title'         => title,
+      'merge_request' => options.merge_request,
+      'author'        => options.author
+    )
+  end
+
+  def write
+    File.write(file_path, contents)
+  end
+
+  def amend_commit
+    %x{git add #{file_path}}
+    exec("git commit --amend")
+  end
+
+  private
+
+  def fail_with(message)
+    $stderr.puts "\e[31merror\e[0m #{message}"
+    exit 1
+  end
+
+  def assert_feature_branch!
+    return unless branch_name == 'master'
+
+    fail_with "Create a branch first!"
+  end
+
+  def assert_new_file!
+    return unless File.exist?(file_path)
+    return if options.force
+
+    fail_with "#{file_path} already exists! Use `--force` to overwrite."
+  end
+
+  def assert_title!
+    return if options.title.length > 0 || options.amend
+
+    fail_with "Provide a title for the changelog entry or use `--amend`" \
+      " to use the title from the previous commit."
+  end
+
+  def title
+    if options.title.empty?
+      last_commit_subject
+    else
+      options.title
+    end
+  end
+
+  def last_commit_subject
+    %x{git log --format="%s" -1}.strip
+  end
+
+  def file_path
+    File.join(
+      unreleased_path,
+      branch_name.gsub(/[^\w-]/, '-') << '.yml'
+    )
+  end
+
+  def unreleased_path
+    File.join('changelogs', 'unreleased').tap do |path|
+      path << '-ee' if ee?
+    end
+  end
+
+  def ee?
+    @ee ||= File.exist?(File.expand_path('../CHANGELOG-EE.md', __dir__))
+  end
+
+  def branch_name
+    @branch_name ||= %x{git symbolic-ref HEAD}.strip.sub(%r{\Arefs/heads/}, '')
+  end
+end
+
+if $0 == __FILE__
+  options = ChangelogOptionParser.parse(ARGV)
+  ChangelogEntry.new(options)
+end
+
+# vim: ft=ruby
diff --git a/changelogs/unreleased/22947-fix_issues_atom_feed_url.yml b/changelogs/unreleased/22947-fix_issues_atom_feed_url.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2312afdb3d7a272bd485c4d298aec09227f4f123
--- /dev/null
+++ b/changelogs/unreleased/22947-fix_issues_atom_feed_url.yml
@@ -0,0 +1,4 @@
+---
+title: Issues atom feed url reflect filters on dashboard
+merge_request: 7114
+author: Lucas Deschamps
diff --git a/changelogs/unreleased/24056-guest-sees-some-project-details-and-gets-404.yml b/changelogs/unreleased/24056-guest-sees-some-project-details-and-gets-404.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8ca0c5beab306a3ef77bed3963c72f3f3e524fe1
--- /dev/null
+++ b/changelogs/unreleased/24056-guest-sees-some-project-details-and-gets-404.yml
@@ -0,0 +1,4 @@
+---
+title: 'Fix: Guest sees some repository details and gets 404'
+merge_request: 
+author: 
diff --git a/changelogs/unreleased/24059-round-robin-repository-storage.yml b/changelogs/unreleased/24059-round-robin-repository-storage.yml
new file mode 100644
index 0000000000000000000000000000000000000000..109536114ff4de37bf04420537c1bf6e31d6d7ef
--- /dev/null
+++ b/changelogs/unreleased/24059-round-robin-repository-storage.yml
@@ -0,0 +1,4 @@
+---
+title: Introduce round-robin project creation to spread load over multiple shards
+merge_request: 7266
+author: 
diff --git a/changelogs/unreleased/24102-cannot-unselect-remove-source-branch-when-editing-merge-request.yml b/changelogs/unreleased/24102-cannot-unselect-remove-source-branch-when-editing-merge-request.yml
new file mode 100644
index 0000000000000000000000000000000000000000..50d018170f1323b5bf53208530979667bbea58c3
--- /dev/null
+++ b/changelogs/unreleased/24102-cannot-unselect-remove-source-branch-when-editing-merge-request.yml
@@ -0,0 +1,4 @@
+---
+title: Ensure merge request's "remove branch" accessors return booleans
+merge_request: 7267
+author:
diff --git a/changelogs/unreleased/fix-invalid-filename-eslint.yml b/changelogs/unreleased/fix-invalid-filename-eslint.yml
new file mode 100644
index 0000000000000000000000000000000000000000..eea21149c907be37ffa4464e2d832dedaa4684b9
--- /dev/null
+++ b/changelogs/unreleased/fix-invalid-filename-eslint.yml
@@ -0,0 +1,4 @@
+---
+title: Fix invalid filename validation on eslint
+merge_request: 7281
+author: 
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index 74325872b09713cc318e1d94d94ff72176adc7e1..c11296975b7c4d92402495f86b6de803f085d2c9 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -101,6 +101,13 @@
     :why: GPL-licensed libraries cannot be linked to from non-GPL projects.
     :versions: []
     :when: 2016-05-02 05:29:43.904715000 Z
+- - :blacklist
+  - OSL-3.0
+  - :who: Sean McGivern
+    :why: The OSL license is a copyleft license
+    :versions: []
+    :when: 2016-10-28 11:02:15.540105000 Z
+
 
 # GEM LICENSES
 - - :license
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 3451b68cea59f611cc6ea7573514f44107cda14b..699ab6075b6e7a13a60b0ee299d90833225096f3 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -432,7 +432,9 @@ production: &base
   ## Repositories settings
   repositories:
     # Paths where repositories can be stored. Give the canonicalized absolute pathname.
-    # NOTE: REPOS PATHS MUST NOT CONTAIN ANY SYMLINK!!!
+    # IMPORTANT: None of the path components may be symlink, because
+    # gitlab-shell invokes Dir.pwd inside the repository path and that results
+    # real path not the symlink.
     storages: # You must have at least a `default` storage path.
       default: /home/git/repositories/
 
diff --git a/config/initializers/0_post_deployment_migrations.rb b/config/initializers/0_post_deployment_migrations.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0068a03d21416d2769a5a1fff0808994a4077445
--- /dev/null
+++ b/config/initializers/0_post_deployment_migrations.rb
@@ -0,0 +1,12 @@
+# Post deployment migrations are included by default. This file must be loaded
+# before other initializers as Rails may otherwise memoize a list of migrations
+# excluding the post deployment migrations.
+unless ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS']
+  path = Rails.root.join('db', 'post_migrate').to_s
+
+  Rails.application.config.paths['db/migrate'] << path
+
+  # Rails memoizes migrations at certain points where it won't read the above
+  # path just yet. As such we must also update the following list of paths.
+  ActiveRecord::Migrator.migrations_paths << path
+end
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index f7e714cd6bc7855a48e268b984a8f3a64e1c4c38..0455a98dbfe991eb66b3d189279101ae7ed34399 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -42,3 +42,19 @@ end
 Sidekiq.configure_client do |config|
   config.redis = redis_config_hash
 end
+
+# The Sidekiq client API always adds the queue to the Sidekiq queue
+# list, but mail_room and gitlab-shell do not. This is only necessary
+# for monitoring.
+config = YAML.load_file(Rails.root.join('config', 'sidekiq_queues.yml').to_s)
+
+begin
+  Sidekiq.redis do |conn|
+    conn.pipelined do
+      config[:queues].each do |queue|
+        conn.sadd('queues', queue[0])
+      end
+    end
+  end
+rescue Redis::BaseError, SocketError
+end
diff --git a/db/migrate/20161103171205_rename_repository_storage_column.rb b/db/migrate/20161103171205_rename_repository_storage_column.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e9f992793b42cc98d4cc45da41963a9e0a9a5a9c
--- /dev/null
+++ b/db/migrate/20161103171205_rename_repository_storage_column.rb
@@ -0,0 +1,29 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RenameRepositoryStorageColumn < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  # Set this constant to true if this migration requires downtime.
+  DOWNTIME = false
+
+  # When a migration requires downtime you **must** uncomment the following
+  # constant and define a short and easy to understand explanation as to why the
+  # migration requires downtime.
+  # DOWNTIME_REASON = ''
+
+  # When using the methods "add_concurrent_index" or "add_column_with_default"
+  # you must disable the use of transactions as these methods can not run in an
+  # existing transaction. When using "add_concurrent_index" make sure that this
+  # method is the _only_ method called in the migration, any other changes
+  # should go in a separate migration. This ensures that upon failure _only_ the
+  # index creation fails and can be retried or reverted easily.
+  #
+  # To disable transactions uncomment the following line and remove these
+  # comments:
+  # disable_ddl_transaction!
+
+  def change
+    rename_column :application_settings, :repository_storage, :repository_storages
+  end
+end
diff --git a/db/post_migrate/.gitkeep b/db/post_migrate/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/db/schema.rb b/db/schema.rb
index 54b5fc83be04a56b5ee5271194a6e88ea38b8ba1..dc088925d978bebc0ec716550059aec9c95ffa09 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20161025231710) do
+ActiveRecord::Schema.define(version: 20161103171205) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -88,7 +88,7 @@ ActiveRecord::Schema.define(version: 20161025231710) do
     t.integer "container_registry_token_expire_delay", default: 5
     t.text "after_sign_up_text"
     t.boolean "user_default_external", default: false, null: false
-    t.string "repository_storage", default: "default"
+    t.string "repository_storages", default: "default"
     t.string "enabled_git_access_protocol"
     t.boolean "domain_blacklist_enabled", default: false
     t.text "domain_blacklist"
diff --git a/doc/administration/img/repository_storages_admin_ui.png b/doc/administration/img/repository_storages_admin_ui.png
index 599350bc098052df2b51da8cb065f246463d8031..6481baca1ad90a76c3f09f554833a5fe9fb366d9 100644
Binary files a/doc/administration/img/repository_storages_admin_ui.png and b/doc/administration/img/repository_storages_admin_ui.png differ
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index 737b39db16ceb1966f6349b1a49af400104df660..d757a3c2a6661c6bd9b91e6a085eb541fe75f5c8 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -13,7 +13,8 @@ This guide talks about how to read and use these system log files.
 
 This file lives in `/var/log/gitlab/gitlab-rails/production.log` for
 omnibus package or in `/home/git/gitlab/log/production.log` for
-installations from source.
+installations from source. (When Gitlab is running in an environment
+other than production, the corresponding logfile is shown here.)
 
 It contains information about all performed requests. You can see the
 URL and type of request, IP address and what exactly parts of code were
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
new file mode 100644
index 0000000000000000000000000000000000000000..f3c2e72341f69db88b044a800671260a92381837
--- /dev/null
+++ b/doc/administration/raketasks/maintenance.md
@@ -0,0 +1,220 @@
+# Maintenance Rake Tasks
+
+## Gather information about GitLab and the system it runs on
+
+This command gathers information about your GitLab installation and the System it runs on. These may be useful when asking for help or reporting issues.
+
+**Omnibus Installation**
+
+```
+sudo gitlab-rake gitlab:env:info
+```
+
+**Source Installation**
+
+```
+bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+Example output:
+
+```
+System information
+System:           Debian 7.8
+Current User:     git
+Using RVM:        no
+Ruby Version:     2.1.5p273
+Gem Version:      2.4.3
+Bundler Version:  1.7.6
+Rake Version:     10.3.2
+Sidekiq Version:  2.17.8
+
+GitLab information
+Version:          7.7.1
+Revision:         41ab9e1
+Directory:        /home/git/gitlab
+DB Adapter:       postgresql
+URL:              https://gitlab.example.com
+HTTP Clone URL:   https://gitlab.example.com/some-project.git
+SSH Clone URL:    git@gitlab.example.com:some-project.git
+Using LDAP:       no
+Using Omniauth:   no
+
+GitLab Shell
+Version:          2.4.1
+Repositories:     /home/git/repositories/
+Hooks:            /home/git/gitlab-shell/hooks/
+Git:              /usr/bin/git
+```
+
+## Check GitLab configuration
+
+Runs the following rake tasks:
+
+- `gitlab:gitlab_shell:check`
+- `gitlab:sidekiq:check`
+- `gitlab:app:check`
+
+It will check that each component was setup according to the installation guide and suggest fixes for issues found.
+
+You may also have a look at our [Trouble Shooting Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide).
+
+**Omnibus Installation**
+
+```
+sudo gitlab-rake gitlab:check
+```
+
+**Source Installation**
+
+```
+bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+NOTE: Use SANITIZE=true for gitlab:check if you want to omit project names from the output.
+
+Example output:
+
+```
+Checking Environment ...
+
+Git configured for git user? ... yes
+Has python2? ... yes
+python2 is supported version? ... yes
+
+Checking Environment ... Finished
+
+Checking GitLab Shell ...
+
+GitLab Shell version? ... OK (1.2.0)
+Repo base directory exists? ... yes
+Repo base directory is a symlink? ... no
+Repo base owned by git:git? ... yes
+Repo base access is drwxrws---? ... yes
+post-receive hook up-to-date? ... yes
+post-receive hooks in repos are links: ... yes
+
+Checking GitLab Shell ... Finished
+
+Checking Sidekiq ...
+
+Running? ... yes
+
+Checking Sidekiq ... Finished
+
+Checking GitLab ...
+
+Database config exists? ... yes
+Database is SQLite ... no
+All migrations up? ... yes
+GitLab config exists? ... yes
+GitLab config outdated? ... no
+Log directory writable? ... yes
+Tmp directory writable? ... yes
+Init script exists? ... yes
+Init script up-to-date? ... yes
+Redis version >= 2.0.0? ... yes
+
+Checking GitLab ... Finished
+```
+
+## Rebuild authorized_keys file
+
+In some case it is necessary to rebuild the `authorized_keys` file.
+
+**Omnibus Installation**
+
+```
+sudo gitlab-rake gitlab:shell:setup
+```
+
+**Source Installation**
+
+```
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:shell:setup RAILS_ENV=production
+```
+
+```
+This will rebuild an authorized_keys file.
+You will lose any data stored in authorized_keys file.
+Do you want to continue (yes/no)? yes
+```
+
+## Clear redis cache
+
+If for some reason the dashboard shows wrong information you might want to
+clear Redis' cache.
+
+**Omnibus Installation**
+
+```
+sudo gitlab-rake cache:clear
+```
+
+**Source Installation**
+
+```
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+## Precompile the assets
+
+Sometimes during version upgrades you might end up with some wrong CSS or
+missing some icons. In that case, try to precompile the assets again.
+
+Note that this only applies to source installations and does NOT apply to
+Omnibus packages.
+
+**Source Installation**
+
+```
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
+```
+
+For omnibus versions, the unoptimized assets (JavaScript, CSS) are frozen at
+the release of upstream GitLab. The omnibus version includes optimized versions
+of those assets. Unless you are modifying the JavaScript / CSS code on your
+production machine after installing the package, there should be no reason to redo
+rake assets:precompile on the production machine. If you suspect that assets
+have been corrupted, you should reinstall the omnibus package.
+
+## Tracking Deployments
+
+GitLab provides a Rake task that lets you track deployments in GitLab
+Performance Monitoring. This Rake task simply stores the current GitLab version
+in the GitLab Performance Monitoring database.
+
+**Omnibus Installation**
+
+```
+sudo gitlab-rake gitlab:track_deployment
+```
+
+**Source Installation**
+
+```
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:track_deployment RAILS_ENV=production
+```
+
+## Create or repair repository hooks symlink
+
+If the GitLab shell hooks directory location changes or another circumstance
+leads to the hooks symlink becoming missing or invalid, run this Rake task
+to create or repair the symlinks.
+
+**Omnibus Installation**
+
+```
+sudo gitlab-rake gitlab:shell:create_hooks
+```
+
+**Source Installation**
+
+```
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:shell:create_hooks RAILS_ENV=production
+```
diff --git a/doc/administration/repository_storages.md b/doc/administration/repository_storages.md
index 55b054fc1a440039384825e5698bccce1ea25743..ab70557b69adc7f3cbd7cc12a3551102615f5d5c 100644
--- a/doc/administration/repository_storages.md
+++ b/doc/administration/repository_storages.md
@@ -91,6 +91,9 @@ be stored via the **Application Settings** in the Admin area.
 
 ![Choose repository storage path in Admin area](img/repository_storages_admin_ui.png)
 
+Beginning with GitLab 8.13.4, multiple paths can be chosen. New projects will be
+randomly placed on one of the selected paths.
+
 [ce-4578]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4578
 [restart gitlab]: restart_gitlab.md#installations-from-source
 [reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure
diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md
index d8dce4388e152a57eba5c4f2d4b362e266ca2498..6f1356ddf8f3225d37bec47c68b394a6e155c043 100644
--- a/doc/administration/troubleshooting/debug.md
+++ b/doc/administration/troubleshooting/debug.md
@@ -107,7 +107,7 @@ downtime. Otherwise skip to the next section.
 1. To see the current threads, run:
 
     ```
-    apply all thread bt
+    thread apply all bt
     ```
 
 1. Once you're done debugging with `gdb`, be sure to detach from the process and exit:
diff --git a/doc/api/README.md b/doc/api/README.md
index 3fbe5197a21f450cf71e8c938344edd74ff8dda7..f65b934b9dbbf35e3792564df059fbd9bee75349 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -23,6 +23,7 @@ following locations:
 - [Group Access Requests](access_requests.md)
 - [Group Members](members.md)
 - [Issues](issues.md)
+- [Issue Boards](boards.md)
 - [Keys](keys.md)
 - [Labels](labels.md)
 - [Merge Requests](merge_requests.md)
diff --git a/doc/api/groups.md b/doc/api/groups.md
index e81d6f9de4b0aca6b7ff8a2cf89040c9be4b5e95..b56d74d25e078a149da6836f2f7e15ecb1a44bb6 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -2,7 +2,12 @@
 
 ## List groups
 
-Get a list of groups. (As user: my groups, as admin: all groups)
+Get a list of groups. (As user: my groups or all available, as admin: all groups).
+
+Parameters:
+
+- `all_available` (optional) - if passed, show all groups you have access to
+- `skip_groups` (optional)(array of group IDs) - if passed, skip groups
 
 ```
 GET /groups
@@ -21,7 +26,6 @@ GET /groups
 
 You can search for groups by name or path, see below.
 
-
 ## List a group's projects
 
 Get a list of projects in this group.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index b69db90e70d8d8f44848ebb5d9439dd4b3911cd2..4f4b20a1874ab1242ac724e13c4581327198906a 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -598,7 +598,7 @@ Parameters:
 | `container_registry_enabled` | boolean | no | Enable container registry for this project |
 | `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
 | `public` | boolean | no | If `true`, the same as setting `visibility_level` to 20 |
-| `visibility_level` | integer | no | See [project visibility level][#project-visibility-level] |
+| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
 | `import_url` | string | no | URL to import repository from |
 | `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
 | `only_allow_merge_if_build_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
@@ -630,7 +630,7 @@ Parameters:
 | `container_registry_enabled` | boolean | no | Enable container registry for this project |
 | `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
 | `public` | boolean | no | If `true`, the same as setting `visibility_level` to 20 |
-| `visibility_level` | integer | no | See [project visibility level][#project-visibility-level] |
+| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
 | `import_url` | string | no | URL to import repository from |
 | `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
 | `only_allow_merge_if_build_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
@@ -661,7 +661,7 @@ Parameters:
 | `container_registry_enabled` | boolean | no | Enable container registry for this project |
 | `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
 | `public` | boolean | no | If `true`, the same as setting `visibility_level` to 20 |
-| `visibility_level` | integer | no | See [project visibility level][#project-visibility-level] |
+| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
 | `import_url` | string | no | URL to import repository from |
 | `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
 | `only_allow_merge_if_build_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
@@ -1139,6 +1139,7 @@ Parameters:
 | `pipeline_events` | boolean | no | Trigger hook on pipeline events |
 | `wiki_events` | boolean | no | Trigger hook on wiki events |
 | `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
+| `token` | string | no | Secret token to validate received payloads; this will not be returned in the response |
 
 ### Edit project hook
 
@@ -1164,6 +1165,7 @@ Parameters:
 | `pipeline_events` | boolean | no | Trigger hook on pipeline events |
 | `wiki_events` | boolean | no | Trigger hook on wiki events |
 | `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
+| `token` | string | no | Secret token to validate received payloads; this will not be returned in the response |
 
 ### Delete project hook
 
diff --git a/doc/api/settings.md b/doc/api/settings.md
index f7ad3b4cc8edb110953d7d732e5f8678a6283984..218546aafea0e296e0bd2d980aa6ef49dffd60d5 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -42,6 +42,7 @@ Example response:
    "sign_in_text" : null,
    "container_registry_token_expire_delay": 5,
    "repository_storage": "default",
+   "repository_storages": ["default"],
    "koding_enabled": false,
    "koding_url": null
 }
@@ -73,7 +74,8 @@ PUT /application/settings
 | `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider |
 | `after_sign_out_path` | string | no | Where to redirect users after logout |
 | `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
-| `repository_storage` | string | no | Storage path for new projects. The value should be the name of one of the repository storage paths defined in your gitlab.yml |
+| `repository_storages` | array of strings | no | A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random. |
+| `repository_storage` | string | no | The first entry in `repository_storages`. Deprecated, but retained for compatibility reasons |
 | `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
 | `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
 | `koding_url` | string | yes (if `koding_enabled` is `true`) |  The Koding instance URL for integration. |
diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md
index 073e99b7147043cef150b8e921b1e6b7983d7d69..efd23d514bc637350b2b2d52076427e30345e427 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -27,11 +27,14 @@ Example response:
 
 ```json
 [
-   {
-      "id" : 1,
-      "url" : "https://gitlab.example.com/hook",
-      "created_at" : "2015-11-04T20:07:35.874Z"
-   }
+  {
+    "id":1,
+    "url":"https://gitlab.example.com/hook",
+    "created_at":"2016-10-31T12:32:15.192Z",
+    "push_events":true,
+    "tag_push_events":false,
+    "enable_ssl_verification":true
+  }
 ]
 ```
 
@@ -48,6 +51,10 @@ POST /hooks
 | Attribute | Type | Required | Description |
 | --------- | ---- | -------- | ----------- |
 | `url` | string | yes | The hook URL |
+| `token` | string | no | Secret token to validate received payloads; this will not be returned in the response |
+| `push_events` | boolean |  no | When true, the hook will fire on push events |
+| `tag_push_events` | boolean | no | When true, the hook will fire on new tags being pushed |
+| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
 
 Example request:
 
@@ -59,11 +66,14 @@ Example response:
 
 ```json
 [
-   {
-      "id" : 2,
-      "url" : "https://gitlab.example.com/hook",
-      "created_at" : "2015-11-04T20:07:35.874Z"
-   }
+  {
+    "id":1,
+    "url":"https://gitlab.example.com/hook",
+    "created_at":"2016-10-31T12:32:15.192Z",
+    "push_events":true,
+    "tag_push_events":false,
+    "enable_ssl_verification":true
+  }
 ]
 ```
 
diff --git a/doc/api/tags.md b/doc/api/tags.md
index 5405911745653f6d0ebe3edfe8231158f87d3dad..398b080e3f672b6f7ffe4ede3806ad8d9aa693d7 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -124,7 +124,7 @@ Parameters:
 The message will be `nil` when creating a lightweight tag otherwise
 it will contain the annotation.
 
-It returns 200 if the operation succeed. In case of an error,
+It returns 201 if the operation succeed. In case of an error,
 405 with an explaining error message is returned.
 
 ## Delete a tag
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index a313c31e7eec4c9bdc1c759e6daac71b835361cc..959741f73389e1ad5679f959762b554f8633c610 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -226,7 +226,7 @@ e.g. `docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_ap
 > **Note:**
 This feature requires GitLab 8.8 and GitLab Runner 1.2.
 
-Once you've built a Docker image, you can push it up to the built-in [GitLab Container Registry](../../container_registry/README.md). For example, if you're using
+Once you've built a Docker image, you can push it up to the built-in [GitLab Container Registry](../../user/project/container_registry.md). For example, if you're using
 docker-in-docker on your runners, this is how your `.gitlab-ci.yml` could look:
 
 
diff --git a/doc/development/README.md b/doc/development/README.md
index 14d6f08e43a7d84248d0d7751e5fb057686eeaa3..bf1f054b7d5fa5aed1dc2085f60e97df95b56e20 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -21,6 +21,7 @@
 
 ## Process
 
+- [Generate a changelog entry with `bin/changelog`](changelog.md)
 - [Code review guidelines](code_review.md) for reviewing code and having code reviewed.
 - [Merge request performance guidelines](merge_request_performance_guidelines.md)
   for ensuring merge requests do not negatively impact GitLab performance
@@ -41,6 +42,7 @@
 
 - [What requires downtime?](what_requires_downtime.md)
 - [Adding database indexes](adding_database_indexes.md)
+- [Post Deployment Migrations](post_deployment_migrations.md)
 
 ## Compliance
 
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
new file mode 100644
index 0000000000000000000000000000000000000000..6a97fae9cac9ca287b84114e39704ce1bd3503a5
--- /dev/null
+++ b/doc/development/changelog.md
@@ -0,0 +1,185 @@
+# Generate a changelog entry
+
+This guide contains instructions for generating a changelog entry data file, as
+well as information and history about our changelog process.
+
+## Overview
+
+Each bullet point, or **entry**, in our [`CHANGELOG.md`][changelog.md] file is
+generated from a single data file in the [`changelogs/unreleased/`][unreleased]
+(or corresponding EE) folder. The file is expected to be a [YAML] file in the
+following format:
+
+```yaml
+---
+title: "Going through change[log]s"
+merge_request: 1972
+author: Ozzy Osbourne
+```
+
+The `merge_request` value is a reference to a merge request that adds this
+entry, and the `author` key is used to give attribution to community
+contributors. Both are optional.
+
+Community contributors and core team members are encouraged to add their name to
+the `author` field. GitLab team members should not.
+
+If you're working on the GitLab EE repository, the entry will be added to
+`changelogs/unreleased-ee/` instead.
+
+[changelog.md]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG.md
+[unreleased]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/changelogs/
+[YAML]: https://en.wikipedia.org/wiki/YAML
+
+## Instructions
+
+A `bin/changelog` script is available to generate the changelog entry file
+automatically.
+
+Its simplest usage is to provide the value for `title`:
+
+```text
+$ bin/changelog 'Hey DZ, I added a feature to GitLab!'
+create changelogs/unreleased/my-feature.yml
+---
+title: Hey DZ, I added a feature to GitLab!
+merge_request:
+author:
+```
+
+The entry filename is based on the name of the current Git branch. If you run
+the command above on a branch called `feature/hey-dz`, it will generate a
+`changelogs/unreleased/feature-hey-dz.yml` file.
+
+### Arguments
+
+| Argument          | Shorthand | Purpose                                       |
+| ----------------- | --------- | --------------------------------------------- |
+| `--amend`         |           | Amend the previous commit                     |
+| `--force`         | `-f`      | Overwrite an existing entry                   |
+| `--merge-request` | `-m`      | Merge Request ID                              |
+| `--dry-run`       | `-n`      | Don't actually write anything, just print     |
+| `--git-username`  | `-u`      | Use Git user.name configuration as the author |
+| `--help`          | `-h`      | Print help message                            |
+
+#### `--amend`
+
+You can pass the **`--amend`** argument to automatically stage the generated
+file and amend it to the previous commit.
+
+If you use **`--amend`** and don't provide a title, it will automatically use
+the "subject" of the previous commit, which is the first line of the commit
+message:
+
+```text
+$ git show --oneline
+ab88683 Added an awesome new feature to GitLab
+
+$ bin/changelog --amend
+create changelogs/unreleased/feature-hey-dz.yml
+---
+title: Added an awesome new feature to GitLab
+merge_request:
+author:
+```
+
+#### `--force` or `-f`
+
+Use **`--force`** or **`-f`** to overwrite an existing changelog entry if it
+already exists.
+
+```text
+$ bin/changelog 'Hey DZ, I added a feature to GitLab!'
+error changelogs/unreleased/feature-hey-dz.yml already exists! Use `--force` to overwrite.
+
+$ bin/changelog 'Hey DZ, I added a feature to GitLab!' --force
+create changelogs/unreleased/feature-hey-dz.yml
+---
+title: Hey DZ, I added a feature to GitLab!
+merge_request: 1983
+author:
+```
+
+#### `--merge-request` or `-m`
+
+Use the **`--merge-request`** or **`-m`** argument to provide the
+`merge_request` value:
+
+```text
+$ bin/changelog 'Hey DZ, I added a feature to GitLab!' -m 1983
+create changelogs/unreleased/feature-hey-dz.yml
+---
+title: Hey DZ, I added a feature to GitLab!
+merge_request: 1983
+author:
+```
+
+#### `--dry-run` or `-n`
+
+Use the **`--dry-run`** or **`-n`** argument to prevent actually writing or
+committing anything:
+
+```text
+$ bin/changelog --amend --dry-run
+create changelogs/unreleased/feature-hey-dz.yml
+---
+title: Added an awesome new feature to GitLab
+merge_request:
+author:
+
+$ ls changelogs/unreleased/
+```
+
+#### `--git-username` or `-u`
+
+Use the **`--git-username`** or **`-u`** argument to automatically fill in the
+`author` value with your configured Git `user.name` value:
+
+```text
+$ git config user.name
+Jane Doe
+
+$ bin/changelog --u 'Hey DZ, I added a feature to GitLab!'
+create changelogs/unreleased/feature-hey-dz.yml
+---
+title: Hey DZ, I added a feature to GitLab!
+merge_request:
+author: Jane Doe
+```
+
+## History and Reasoning
+
+Our `CHANGELOG` file was previously updated manually by each contributor that
+felt their change warranted an entry. When two merge requests added their own
+entries at the same spot in the list, it created a merge conflict in one as soon
+as the other was merged. When we had dozens of merge requests fighting for the
+same changelog entry location, this quickly became a major source of merge
+conflicts and delays in development.
+
+This led us to a [boring solution] of "add your entry in a random location in
+the list." This actually worked pretty well as we got further along in each
+monthly release cycle, but at the start of a new cycle, when a new version
+section was added and there were fewer places to "randomly" add an entry, the
+conflicts became a problem again until we had a sufficient number of entries.
+
+On top of all this, it created an entirely different headache for [release managers]
+when they cherry-picked a commit into a stable branch for a patch release. If
+the commit included an entry in the `CHANGELOG`, it would include the entire
+changelog for the latest version in `master`, so the release manager would have
+to manually remove the later entries. They often would have had to do this
+multiple times per patch release. This was compounded when we had to release
+multiple patches at once due to a security issue.
+
+We needed to automate all of this manual work. So we [started brainstorming].
+After much discussion we settled on the current solution of one file per entry,
+and then compiling the entries into the overall `CHANGELOG.md` file during the
+[release process].
+
+[boring solution]: https://about.gitlab.com/handbook/#boring-solutions
+[release managers]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/release-manager.md
+[started brainstorming]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17826
+[release process]: https://gitlab.com/gitlab-org/release-tools
+
+---
+
+[Return to Development documentation](README.md)
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index 2cfa30f652efc650eb5085b50818e76bced3032c..b137e6ae82e98e0c02a7c72d84481da2ce44e5d6 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -465,6 +465,7 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "domain
 [cURL]: http://curl.haxx.se/ "cURL website"
 [single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html
 [gfm]: http://docs.gitlab.com/ce/user/markdown.html#newlines "GitLab flavored markdown documentation"
+[ce-1242]: https://gitlab.com/gitlab-org/gitlab-ce/issues/1242
 [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
diff --git a/doc/development/frontend.md b/doc/development/frontend.md
index ece8f8805426be68254764d86a1e2123cbd88022..1d7d9127a646767203ba8506ea7e8e6fdcb605de 100644
--- a/doc/development/frontend.md
+++ b/doc/development/frontend.md
@@ -196,6 +196,12 @@ It consists of two subtasks:
 As long as the fixtures don't change, `rake teaspoon:tests` is sufficient
 (and saves you some time).
 
+If you need to debug your tests and/or application code while they're
+running, navigate to [localhost:3000/teaspoon](http://localhost:3000/teaspoon)
+in your browser, open DevTools, and run tests for individual files by clicking 
+on them. This is also much faster than setting up and running tests from the 
+command line.
+
 Please note: Not all of the frontend fixtures are generated. Some are still static
 files. These will not be touched by `rake teaspoon:fixtures`.
 
diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md
index 159d5ce286db39ed0fe2151f76adf42646236df6..b25ce79e89f25da4a1b7763cc735288b33869194 100644
--- a/doc/development/gotchas.md
+++ b/doc/development/gotchas.md
@@ -41,9 +41,9 @@ Rubocop](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/.rubocop.yml#L9
 
 [Exception]: http://stackoverflow.com/q/10048173/223897
 
-## Don't use inline CoffeeScript/JavaScript in views
+## Don't use inline JavaScript in views
 
-Using the inline `:coffee` or `:coffeescript` Haml filters comes with a
+Using the inline `:javascript` Haml filters comes with a
 performance overhead. Using inline JavaScript is not a good way to structure your code and should be avoided.
 
 _**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/hamlit.rb)
@@ -51,9 +51,7 @@ in an initializer._
 
 ### Further reading
 
-- Pull Request: [Replace CoffeeScript block into JavaScript in Views](https://git.io/vztMu)
 - Stack Overflow: [Why you should not write inline JavaScript](http://programmers.stackexchange.com/questions/86589/why-should-i-avoid-inline-scripting)
-- Stack Overflow: [Performance implications of using :coffescript filter inside HAML templates?](http://stackoverflow.com/a/17571242/223897)
 
 ## ID-based CSS selectors need to be a bit more specific
 
diff --git a/doc/development/licensing.md b/doc/development/licensing.md
index 05972b33fdb80d502a704abc7f7a9b1330b158c2..5d177eb26eefe42c2a4f4bc978640e042487472b 100644
--- a/doc/development/licensing.md
+++ b/doc/development/licensing.md
@@ -62,6 +62,7 @@ Libraries with the following licenses are unacceptable for use:
 
 - [GNU GPL][GPL] (version 1, [version 2][GPLv2], [version 3][GPLv3], or any future versions): GPL-licensed libraries cannot be linked to from non-GPL projects.
 - [GNU AGPLv3][AGPLv3]: AGPL-licensed libraries cannot be linked to from non-GPL projects.
+- [Open Software License (OSL)][OSL]: is a copyleft license. In addition, the FSF [recommend against its use][OSL-GNU].
 
 ## Notes
 
@@ -93,3 +94,5 @@ Gems which are included only in the "development" or "test" groups by Bundler ar
 [AGPLv3]: http://choosealicense.com/licenses/agpl-3.0/
 [GNU-GPL-FAQ]: http://www.gnu.org/licenses/gpl-faq.html#IfLibraryIsGPL
 [OSI-GPL]: https://opensource.org/faq#linking-proprietary-code
+[OSL]: https://opensource.org/licenses/OSL-3.0
+[OSL-GNU]: https://www.gnu.org/licenses/license-list.en.html#OSL
diff --git a/doc/development/post_deployment_migrations.md b/doc/development/post_deployment_migrations.md
new file mode 100644
index 0000000000000000000000000000000000000000..cfc91539beee3cc2649cd0149e2931439252374f
--- /dev/null
+++ b/doc/development/post_deployment_migrations.md
@@ -0,0 +1,75 @@
+# Post Deployment Migrations
+
+Post deployment migrations are regular Rails migrations that can optionally be
+executed after a deployment. By default these migrations are executed alongside
+the other migrations. To skip these migrations you will have to set the
+environment variable `SKIP_POST_DEPLOYMENT_MIGRATIONS` to a non-empty value
+when running `rake db:migrate`.
+
+For example, this would run all migrations including any post deployment
+migrations:
+
+```bash
+bundle exec rake db:migrate
+```
+
+This however will skip post deployment migrations:
+
+```bash
+SKIP_POST_DEPLOYMENT_MIGRATIONS=true bundle exec rake db:migrate
+```
+
+## Deployment Integration
+
+Say you're using Chef for deploying new versions of GitLab and you'd like to run
+post deployment migrations after deploying a new version. Let's assume you
+normally use the command `chef-client` to do so. To make use of this feature
+you'd have to run this command as follows:
+
+```bash
+SKIP_POST_DEPLOYMENT_MIGRATIONS=true sudo chef-client
+```
+
+Once all servers have been updated you can run `chef-client` again on a single
+server _without_ the environment variable.
+
+The process is similar for other deployment techniques: first you would deploy
+with the environment variable set, then you'll essentially re-deploy a single
+server but with the variable _unset_.
+
+## Creating Migrations
+
+To create a post deployment migration you can use the following Rails generator:
+
+```bash
+bundle exec rails g post_deployment_migration migration_name_here
+```
+
+This will generate the migration file in `db/post_migrate`. These migrations
+behave exactly like regular Rails migrations.
+
+## Use Cases
+
+Post deployment migrations can be used to perform migrations that mutate state
+that an existing version of GitLab depends on. For example, say you want to
+remove a column from a table. This requires downtime as a GitLab instance
+depends on this column being present while it's running. Normally you'd follow
+these steps in such a case:
+
+1. Stop the GitLab instance
+2. Run the migration removing the column
+3. Start the GitLab instance again
+
+Using post deployment migrations we can instead follow these steps:
+
+1. Deploy a new version of GitLab while ignoring post deployment migrations
+2. Re-run `rake db:migrate` but without the environment variable set
+
+Here we don't need any downtime as the migration takes place _after_ a new
+version (which doesn't depend on the column anymore) has been deployed.
+
+Some other examples where these migrations are useful:
+
+* Cleaning up data generated due to a bug in GitLab
+* Removing tables
+* Migrating jobs from one Sidekiq queue to another
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index a7175f3f87e708b836c626ddfe5e93214a7be3d2..827db7e99b88cdf6fabeb3b709ad87eeac631773 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -42,14 +42,6 @@ To run several tests inside one directory:
 If you want to use [Spring](https://github.com/rails/spring) set
 `ENABLE_SPRING=1` in your environment.
 
-## Generate searchable docs for source code
-
-You can find results under the `doc/code` directory.
-
-```
-bundle exec rake gitlab:generate_docs
-```
-
 ## Generate API documentation for project services (e.g. Slack)
 
 ```
diff --git a/doc/development/testing.md b/doc/development/testing.md
index 513457d203a4875d75d6618c48391a9765ea5290..b0b26ccf57adbb82ec0977d31cc115ebc49e87f4 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -36,8 +36,8 @@ the command line via `bundle exec teaspoon`, or via a web browser at
 `http://localhost:3000/teaspoon` when the Rails server is running.
 
 - JavaScript tests live in `spec/javascripts/`, matching the folder structure of
-  `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js.coffee` has a corresponding
-  `spec/javascripts/behaviors/autosize_spec.js.coffee` file.
+  `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js.es6` has a corresponding
+  `spec/javascripts/behaviors/autosize_spec.js.es6` file.
 - Haml fixtures required for JavaScript tests live in
   `spec/javascripts/fixtures`. They should contain the bare minimum amount of
   markup necessary for the test.
@@ -132,6 +132,42 @@ Adding new Spinach scenarios is acceptable _only if_ the new scenario requires
 no more than one new `step` definition. If more than that is required, the
 test should be re-implemented using RSpec instead.
 
+## Testing Rake Tasks
+
+To make testing Rake tasks a little easier, there is a helper that can be included
+in lieu of the standard Spec helper. Instead of `require 'spec_helper'`, use
+`require 'rake_helper'`. The helper includes `spec_helper` for you, and configures
+a few other things to make testing Rake tasks easier.
+
+At a minimum, requiring the Rake helper will redirect `stdout`, include the
+runtime task helpers, and include the `RakeHelpers` Spec support module.
+
+The `RakeHelpers` module exposes a `run_rake_task(<task>)` method to make
+executing tasks simple. See `spec/support/rake_helpers.rb` for all available
+methods.
+
+Example:
+
+```ruby
+require 'rake_helper'
+
+describe 'gitlab:shell rake tasks' do
+  before do
+    Rake.application.rake_require 'tasks/gitlab/shell'
+
+    stub_warn_user_is_not_gitlab
+  end
+
+ describe 'install task' do
+    it 'invokes create_hooks task' do
+      expect(Rake::Task['gitlab:shell:create_hooks']).to receive(:invoke)
+
+      run_rake_task('gitlab:shell:install')
+    end
+  end
+end
+```
+
 ---
 
 [Return to Development documentation](README.md)
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 795e1d234430543d9d07692fac29901ebeb2de71..7e947e4b2bad733095ca38abc576cd2430278885 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -271,9 +271,9 @@ sudo usermod -aG redis git
 ### Clone the Source
 
     # Clone GitLab repository
-    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-13-stable gitlab
+    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-14-stable gitlab
 
-**Note:** You can change `8-13-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `8-14-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
 
 ### Configure It
 
@@ -479,10 +479,14 @@ Copy the example site config:
     sudo cp lib/support/nginx/gitlab /etc/nginx/sites-available/gitlab
     sudo ln -s /etc/nginx/sites-available/gitlab /etc/nginx/sites-enabled/gitlab
 
-Make sure to edit the config file to match your setup:
+Make sure to edit the config file to match your setup. Also, ensure that you match your paths to GitLab, especially if installing for a user other than the 'git' user:
 
     # Change YOUR_SERVER_FQDN to the fully-qualified
     # domain name of your host serving GitLab.
+    #
+    # Remember to match your paths to GitLab, especially
+    # if installing for a user other than 'git'.
+    #
     # If using Ubuntu default nginx install:
     # either remove the default_server from the listen line
     # or else sudo rm -f /etc/nginx/sites-enabled/default
diff --git a/doc/integration/jira.md b/doc/integration/jira.md
index cf1557ddc44bb3d7b00c5df848dd9651c563c10d..2e31fd994debab446b022066e45efe7e39f61c5d 100644
--- a/doc/integration/jira.md
+++ b/doc/integration/jira.md
@@ -135,7 +135,7 @@ password as they will be needed when configuring GitLab in the next section.
 
 JIRA configuration in GitLab is done via a project's **Services**.
 
-#### GitLab 13.0 with JIRA v1000.x
+#### GitLab 8.13.0 with JIRA v1000.x
 
 To enable JIRA integration in a project, navigate to the project's
 and open the context menu clicking on the top right gear icon, then go to
@@ -160,7 +160,7 @@ with the linked JIRA project.
 
 #### GitLab 6.x-7.7 with JIRA v6.x
 
-_**Note:** GitLab versions 13.0 and up contain various integration improvements.
+_**Note:** GitLab versions 8.13.0 and up contain various integration improvements.
 We strongly recommend upgrading._
 
 In `gitlab.yml` enable the JIRA issue tracker section by
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index f3b2a2887769f6e3c36d24b71c61758743841af0..4a242c321aa8c3e71b4d9fb62e95987dbc190f83 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -268,13 +268,20 @@ message `Can't verify CSRF token authenticity`. This means that there is an erro
 the SAML request, but this error never reaches GitLab due to the CSRF check.
 
 To bypass this you can add `skip_before_action :verify_authenticity_token` to the
-`omniauth_callbacks_controller.rb` file. This will allow the error to hit GitLab,
-where it can then be seen in the usual logs, or as a flash message in the login
-screen.
-
-That file is located at `/opt/gitlab/embedded/service/gitlab-rails/app/controllers`
-for Omnibus installations and by default on `/home/git/gitlab/app/controllers` for
-installations from source.
+`omniauth_callbacks_controller.rb` file immediately after the `class` line and
+comment out the `protect_from_forgery` line using a `#` then restart Unicorn. This
+will allow the error to hit GitLab, where it can then be seen in the usual logs,
+or as a flash message on the login screen.
+
+That file is located in `/opt/gitlab/embedded/service/gitlab-rails/app/controllers`
+for Omnibus installations and by default in `/home/git/gitlab/app/controllers` for
+installations from source. Restart Unicorn using the `sudo gitlab-ctl restart unicorn`
+command on Omnibus installations and `sudo service gitlab restart` on installations
+from source.
+
+You may also find the [SSO Tracer](https://addons.mozilla.org/en-US/firefox/addon/sso-tracer)
+(Firefox) and [SAML Chrome Panel](https://chrome.google.com/webstore/detail/saml-chrome-panel/paijfdbeoenhembfhkhllainmocckace)
+(Chrome) browser extensions useful in your debugging.
 
 ### Invalid audience
 
diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md
index 315cb56a089b6aa0aff79bf3a3c58e7a6e7aff1f..266aeb7d60e6f2eab6ef4f89dde7a732eaf46036 100644
--- a/doc/raketasks/maintenance.md
+++ b/doc/raketasks/maintenance.md
@@ -1,188 +1,3 @@
-# Maintenance
+# Maintenance Rake Tasks
 
-## Gather information about GitLab and the system it runs on
-
-This command gathers information about your GitLab installation and the System it runs on. These may be useful when asking for help or reporting issues.
-
-```
-# omnibus-gitlab
-sudo gitlab-rake gitlab:env:info
-
-# installation from source
-bundle exec rake gitlab:env:info RAILS_ENV=production
-```
-
-Example output:
-
-```
-System information
-System:           Debian 7.8
-Current User:     git
-Using RVM:        no
-Ruby Version:     2.1.5p273
-Gem Version:      2.4.3
-Bundler Version:  1.7.6
-Rake Version:     10.3.2
-Sidekiq Version:  2.17.8
-
-GitLab information
-Version:          7.7.1
-Revision:         41ab9e1
-Directory:        /home/git/gitlab
-DB Adapter:       postgresql
-URL:              https://gitlab.example.com
-HTTP Clone URL:   https://gitlab.example.com/some-project.git
-SSH Clone URL:    git@gitlab.example.com:some-project.git
-Using LDAP:       no
-Using Omniauth:   no
-
-GitLab Shell
-Version:          2.4.1
-Repositories:     /home/git/repositories/
-Hooks:            /home/git/gitlab-shell/hooks/
-Git:              /usr/bin/git
-```
-
-## Check GitLab configuration
-
-Runs the following rake tasks:
-
-- `gitlab:gitlab_shell:check`
-- `gitlab:sidekiq:check`
-- `gitlab:app:check`
-
-It will check that each component was setup according to the installation guide and suggest fixes for issues found.
-
-You may also have a look at our [Trouble Shooting Guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide).
-
-```
-# omnibus-gitlab
-sudo gitlab-rake gitlab:check
-
-# installation from source
-bundle exec rake gitlab:check RAILS_ENV=production
-```
-
-NOTE: Use SANITIZE=true for gitlab:check if you want to omit project names from the output.
-
-Example output:
-
-```
-Checking Environment ...
-
-Git configured for git user? ... yes
-Has python2? ... yes
-python2 is supported version? ... yes
-
-Checking Environment ... Finished
-
-Checking GitLab Shell ...
-
-GitLab Shell version? ... OK (1.2.0)
-Repo base directory exists? ... yes
-Repo base directory is a symlink? ... no
-Repo base owned by git:git? ... yes
-Repo base access is drwxrws---? ... yes
-post-receive hook up-to-date? ... yes
-post-receive hooks in repos are links: ... yes
-
-Checking GitLab Shell ... Finished
-
-Checking Sidekiq ...
-
-Running? ... yes
-
-Checking Sidekiq ... Finished
-
-Checking GitLab ...
-
-Database config exists? ... yes
-Database is SQLite ... no
-All migrations up? ... yes
-GitLab config exists? ... yes
-GitLab config outdated? ... no
-Log directory writable? ... yes
-Tmp directory writable? ... yes
-Init script exists? ... yes
-Init script up-to-date? ... yes
-Redis version >= 2.0.0? ... yes
-
-Checking GitLab ... Finished
-```
-
-## Rebuild authorized_keys file
-
-In some case it is necessary to rebuild the `authorized_keys` file.
-
-For Omnibus-packages:
-```
-sudo gitlab-rake gitlab:shell:setup
-```
-
-For installations from source:
-```
-cd /home/git/gitlab
-sudo -u git -H bundle exec rake gitlab:shell:setup RAILS_ENV=production
-```
-
-```
-This will rebuild an authorized_keys file.
-You will lose any data stored in authorized_keys file.
-Do you want to continue (yes/no)? yes
-```
-
-## Clear redis cache
-
-If for some reason the dashboard shows wrong information you might want to
-clear Redis' cache.
-
-For Omnibus-packages:
-```
-sudo gitlab-rake cache:clear
-```
-
-For installations from source:
-```
-cd /home/git/gitlab
-sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
-```
-
-## Precompile the assets
-
-Sometimes during version upgrades you might end up with some wrong CSS or
-missing some icons. In that case, try to precompile the assets again.
-
-Note that this only applies to source installations and does NOT apply to
-omnibus packages.
-
-For installations from source:
-```
-cd /home/git/gitlab
-sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
-```
-
-For omnibus versions, the unoptimized assets (JavaScript, CSS) are frozen at
-the release of upstream GitLab. The omnibus version includes optimized versions
-of those assets. Unless you are modifying the JavaScript / CSS code on your
-production machine after installing the package, there should be no reason to redo
-rake assets:precompile on the production machine. If you suspect that assets
-have been corrupted, you should reinstall the omnibus package.
-
-## Tracking Deployments
-
-GitLab provides a Rake task that lets you track deployments in GitLab
-Performance Monitoring. This Rake task simply stores the current GitLab version
-in the GitLab Performance Monitoring database.
-
-For Omnibus-packages:
-
-```
-sudo gitlab-rake gitlab:track_deployment
-```
-
-For installations from source:
-
-```
-cd /home/git/gitlab
-sudo -u git -H bundle exec rake gitlab:track_deployment RAILS_ENV=production
-```
+This document was moved to [administration/raketasks/maintenance](../administration/raketasks/maintenance.md).
diff --git a/doc/university/README.md b/doc/university/README.md
index f5a0dab39fe92dbba3aa48ab50ae332b5cd11f47..510b753f70df397fbc9ebbc0ef190439e3a30e75 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -2,7 +2,7 @@
 
 GitLab University is the best place to learn about **Version Control with Git and GitLab**.
 
-It doesn't replace, but accompanies our great [Documentation](http://docs.gitlab.com)
+It doesn't replace, but accompanies our great [Documentation](https://docs.gitlab.com)
 and [Blog Articles](https://about.gitlab.com/blog/).
 
 Would you like to contribute to GitLab University? Then please take a look at our contribution [process](/process) for more information.
@@ -31,7 +31,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
 
 1. [An Overview of GitLab.com - Video](https://www.youtube.com/watch?v=WaiL5DGEMR4)
 1. [Why Use Git and GitLab - Slides](https://docs.google.com/a/gitlab.com/presentation/d/1RcZhFmn5VPvoFu6UMxhMOy7lAsToeBZRjLRn0LIdaNc/edit?usp=drive_web)
-1. [GitLab Basics - Article](http://doc.gitlab.com/ce/gitlab-basics/README.html)
+1. [GitLab Basics - Article](../gitlab-basics/README.md)
 1. [Git and GitLab Basics - Video](https://www.youtube.com/watch?v=03wb9FvO4Ak&index=5&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
 1. [Git and GitLab Basics - Online Course](https://courses.platzi.com/classes/git-gitlab/concepto/part-1/part-23370/material/)
 1. [Comparison of GitLab Versions](https://about.gitlab.com/features/#compare)
@@ -51,10 +51,10 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
 
 #### 1.5. Migrating from other Source Control
 
-1. [Migrating from BitBucket/Stash](http://doc.gitlab.com/ee/workflow/importing/import_projects_from_bitbucket.html)
-1. [Migrating from GitHub](http://doc.gitlab.com/ee/workflow/importing/import_projects_from_github.html)
-1. [Migrating from SVN](http://doc.gitlab.com/ee/workflow/importing/migrating_from_svn.html)
-1. [Migrating from Fogbugz](http://doc.gitlab.com/ee/workflow/importing/import_projects_from_fogbugz.html)
+1. [Migrating from BitBucket/Stash](https://docs.gitlab.com/ee/workflow/importing/import_projects_from_bitbucket.html)
+1. [Migrating from GitHub](https://docs.gitlab.com/ee/workflow/importing/import_projects_from_github.html)
+1. [Migrating from SVN](https://docs.gitlab.com/ee/workflow/importing/migrating_from_svn.html)
+1. [Migrating from Fogbugz](https://docs.gitlab.com/ee/workflow/importing/import_projects_from_fogbugz.html)
 
 #### 1.6. GitLab Inc.
 
@@ -91,11 +91,11 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
 
 1. [Using any Static Site Generator with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
 1. [Securing GitLab Pages with SSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/)
-1. [GitLab Pages Documentation](http://doc.gitlab.com/ee/pages/README.html)
+1. [GitLab Pages Documentation](https://docs.gitlab.com/ee/pages/README.html)
 
 #### 2.2. GitLab Issues
 
-1. [Markdown in GitLab](http://doc.gitlab.com/ce/markdown/markdown.html)
+1. [Markdown in GitLab](../user/markdown.md)
 1. [Issues and Merge Requests - Video](https://www.youtube.com/watch?v=raXvuwet78M)
 1. [Due Dates and Milestones fro GitLab Issues](https://about.gitlab.com/2016/08/05/feature-highlight-set-dates-for-issues/)
 1. [How to Use GitLab Labels](https://about.gitlab.com/2016/08/17/using-gitlab-labels/)
@@ -129,7 +129,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
 1. [GitLab Flow vs Forking in GitLab - Video](https://www.youtube.com/watch?v=UGotqAUACZA)
 1. [GitLab Flow Overview](https://about.gitlab.com/2014/09/29/gitlab-flow/)
 1. [Always Start with an Issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/)
-1. [GitLab Flow Documentation](http://doc.gitlab.com/ee/workflow/gitlab_flow.html)
+1. [GitLab Flow Documentation](https://docs.gitlab.com/ee/workflow/gitlab_flow.html)
 
 #### 2.5. GitLab Comparisons
 
@@ -189,8 +189,8 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
 #### 3.9. <a name="integrations"></a> Integrations
 
 1. [How to Integrate JIRA and Jenkins with GitLab - Video](https://gitlabmeetings.webex.com/gitlabmeetings/ldr.php?RCID=44b548147a67ab4d8a62274047146415)
-1. [How to Integrate Jira with GitLab](http://doc.gitlab.com/ee/integration/jira.html)
-1. [How to Integrate Jenkins with GitLab](http://doc.gitlab.com/ee/integration/jenkins.html)
+1. [How to Integrate Jira with GitLab](https://docs.gitlab.com/ee/integration/jira.html)
+1. [How to Integrate Jenkins with GitLab](https://docs.gitlab.com/ee/integration/jenkins.html)
 1. [How to Integrate Bamboo with GitLab](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/project_services/bamboo.md)
 1. [How to Integrate Slack with GitLab](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/slack.md)
 1. [How to Integrate Convox with GitLab](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/)
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index cf836667fac4905e5bc3ca3cdeea6e16dfea0871..20e7ea1987f2e7fa12638e28314031d9ef88f3b2 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -10,7 +10,7 @@ User authentication by combination of 2 different steps during login. This allow
 
 ### Access Levels
 
-Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. See [GitLab's Permission Guidelines](http://doc.gitlab.com/ce/permissions/permissions.html)
+Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. See [GitLab's Permission Guidelines](../../permissions/permissions.md
 
 ### Active Directory (AD)
 
diff --git a/doc/university/support/README.md b/doc/university/support/README.md
index da991e56370808c3fc594a924c2d0ce0913c64ee..6e415e4d219250a10a1982427c35779833bd0f8f 100644
--- a/doc/university/support/README.md
+++ b/doc/university/support/README.md
@@ -58,28 +58,28 @@ Sometimes we need to upgrade customers from old versions of GitLab to latest, so
     - Users
     - Groups
     - Projects
-  - [Backup using our Backup rake task](http://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system)
+  - [Backup using our Backup rake task](https://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system)
   - [Upgrade to 5.0 source using our Upgrade documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/4.2-to-5.0.md)
   - [Upgrade to 5.1 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.0-to-5.1.md)
   - [Upgrade to 6.0 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.1-to-6.0.md)
   - [Upgrade to 7.14 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/6.x-or-7.x-to-7.14.md)
-  - [Backup using our Backup rake task](http://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system)
-  - [Perform the MySQL to PostgreSQL migration to convert your backup](http://docs.gitlab.com/ce/update/mysql_to_postgresql.html#converting-a-gitlab-backup-file-from-mysql-to-postgres)
-  - [Upgrade to Omnibus 7.14](http://doc.gitlab.com/omnibus/update/README.html#upgrading-from-a-non-omnibus-installation-to-an-omnibus-installation)
-  - [Restore backup using our Restore rake task](http://docs.gitlab.com/ce/raketasks/backup_restore.html#restore-a-previously-created-backup)
+  - [Backup using our Backup rake task](https://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system)
+  - [Perform the MySQL to PostgreSQL migration to convert your backup](https://docs.gitlab.com/ce/update/mysql_to_postgresql.html#converting-a-gitlab-backup-file-from-mysql-to-postgres)
+  - [Upgrade to Omnibus 7.14](https://docs.gitlab.com/omnibus/update/README.html#upgrading-from-a-non-omnibus-installation-to-an-omnibus-installation)
+  - [Restore backup using our Restore rake task](https://docs.gitlab.com/ce/raketasks/backup_restore.html#restore-a-previously-created-backup)
   - [Upgrade to latest EE](https://about.gitlab.com/downloads-ee)
     - (GitLab inc. only) Acquire and apply a license for the Enterprise Edition product, ask in #support
-- Perform a downgrade from [EE to CE](http://doc.gitlab.com/ee/downgrade_ee_to_ce/README.html)
+- Perform a downgrade from [EE to CE](https://docs.gitlab.com/ee/downgrade_ee_to_ce/README.html)
 
 #### Start to learn about some of the integrations that we support
 
 Our integrations add great value to GitLab. User questions often relate to integrating GitLab with existing external services and the configuration involved
 
 - Learn about our Integrations (specially, not only):
-  - [LDAP](http://doc.gitlab.com/ee/integration/ldap.html)
-  - [JIRA](http://doc.gitlab.com/ee/project_services/jira.html)
-  - [Jenkins](http://doc.gitlab.com/ee/integration/jenkins.html)
-  - [SAML](http://doc.gitlab.com/ce/integration/saml.html)
+  - [LDAP](https://docs.gitlab.com/ee/integration/ldap.html)
+  - [JIRA](https://docs.gitlab.com/ee/project_services/jira.html)
+  - [Jenkins](https://docs.gitlab.com/ee/integration/jenkins.html)
+  - [SAML](https://docs.gitlab.com/ce/integration/saml.html)
 
 #### Goals
 
@@ -91,8 +91,8 @@ Our integrations add great value to GitLab. User questions often relate to integ
 #### Understand the gathering of diagnostics for GitLab instances
 
 - Learn about the GitLab checks that are available
-  - [Environment Information and maintenance checks](http://docs.gitlab.com/ce/raketasks/maintenance.html)
-  - [GitLab check](http://docs.gitlab.com/ce/raketasks/check.html)
+  - [Environment Information and maintenance checks](https://docs.gitlab.com/ce/raketasks/maintenance.html)
+  - [GitLab check](https://docs.gitlab.com/ce/raketasks/check.html)
   - Omnibus commands
     - [Status](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#get-service-status)
     - [Starting and stopping services](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#starting-and-stopping)
@@ -167,12 +167,12 @@ Some tickets need specific knowledge or a deep understanding of a particular com
 
 Move on to understanding some of GitLab's more advanced features. You can make use of GitLab.com to understand the features from an end-user perspective and then use your own instance to understand setup and configuration of the feature from an Administrative perspective
 
-- Set up and try [Git Annex](http://doc.gitlab.com/ee/workflow/git_annex.html)
-- Set up and try [Git LFS](http://doc.gitlab.com/ee/workflow/lfs/manage_large_binaries_with_git_lfs.html)
-- Get to know the [GitLab API](http://doc.gitlab.com/ee/api/README.html), its capabilities and shortcomings
-- Learn how to [migrate from SVN to Git](http://doc.gitlab.com/ee/workflow/importing/migrating_from_svn.html)
-- Set up [GitLab CI](http://doc.gitlab.com/ee/ci/quick_start/README.html)
-- Create your first [GitLab Page](http://doc.gitlab.com/ee/pages/administration.html)
+- Set up and try [Git Annex](https://docs.gitlab.com/ee/workflow/git_annex.html)
+- Set up and try [Git LFS](https://docs.gitlab.com/ee/workflow/lfs/manage_large_binaries_with_git_lfs.html)
+- Get to know the [GitLab API](https://docs.gitlab.com/ee/api/README.html), its capabilities and shortcomings
+- Learn how to [migrate from SVN to Git](https://docs.gitlab.com/ee/workflow/importing/migrating_from_svn.html)
+- Set up [GitLab CI](https://docs.gitlab.com/ee/ci/quick_start/README.html)
+- Create your first [GitLab Page](https://docs.gitlab.com/ee/pages/administration.html)
 - Get to know the GitLab Codebase by reading through the source code:
   - Find the differences between the [EE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
      and the [CE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
diff --git a/doc/update/8.13-to-8.14.md b/doc/update/8.13-to-8.14.md
new file mode 100644
index 0000000000000000000000000000000000000000..787511fd6cfbe8082c35cb97cb00e516300fc6b4
--- /dev/null
+++ b/doc/update/8.13-to-8.14.md
@@ -0,0 +1,205 @@
+# From 8.13 to 8.14
+
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+    sudo service gitlab stop
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+We will continue supporting Ruby < 2.3 for the time being but we recommend you
+upgrade to Ruby 2.3 if you're running a source installation, as this is the same
+version that ships with our Omnibus package.
+
+You can check which version you are running with `ruby -v`.
+
+Download and compile Ruby:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
+echo 'c39b4001f7acb4e334cb60a0f4df72d434bef711  ruby-2.3.1.tar.gz' | shasum --check - && tar xzf ruby-2.3.1.tar.gz
+cd ruby-2.3.1
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 8-14-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-14-stable-ee
+```
+
+### 5. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v4.0.0
+```
+
+### 6. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires
+[Go 1.5](https://golang.org/dl) which should already be on your system from
+GitLab 8.1.
+
+```bash
+cd /home/git/gitlab-workhorse
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout v0.8.5
+sudo -u git -H make
+```
+
+### 7. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+```
+
+### 8. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+git diff origin/8-13-stable:config/gitlab.yml.example origin/8-14-stable:config/gitlab.yml.example
+```
+
+#### Git configuration
+
+Configure Git to generate packfile bitmaps (introduced in Git 2.0) on
+the GitLab server during `git gc`.
+
+```sh
+sudo -u git -H git config --global repack.writeBitmaps true
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+# For HTTPS configurations
+git diff origin/8-13-stable:lib/support/nginx/gitlab-ssl origin/8-14-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/8-13-stable:lib/support/nginx/gitlab origin/8-14-stable:lib/support/nginx/gitlab
+```
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-14-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-14-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+Ensure you're still up-to-date with the latest init script changes:
+
+    sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+
+For Ubuntu 16.04.1 LTS:
+
+    sudo systemctl daemon-reload
+
+### 9. Start application
+
+    sudo service gitlab start
+    sudo service nginx restart
+
+### 10. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+    sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check:
+
+    sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.13)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.12 to 8.13](8.12-to-8.13.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
diff --git a/doc/update/README.md b/doc/update/README.md
index 975d72164b4eb050ff53b5e56cd87ca7ec9af8f1..837b31abb979e4161643d7965f5f49e6fc814be8 100644
--- a/doc/update/README.md
+++ b/doc/update/README.md
@@ -85,6 +85,8 @@ possible.
 - [MySQL installation guide](../install/database_mysql.md) contains additional
   information about configuring GitLab to work with a MySQL database.
 - [Restoring from backup after a failed upgrade](restore_after_failure.md)
+- [Upgrading PostgreSQL Using Slony](upgrading_postgresql_using_slony.md), for
+  upgrading a PostgreSQL database with minimal downtime.
 
 [omnidocker]: http://docs.gitlab.com/omnibus/docker/README.html
 [source-ee]: https://gitlab.com/gitlab-org/gitlab-ee/tree/master/doc/update
diff --git a/doc/update/upgrading_postgresql_using_slony.md b/doc/update/upgrading_postgresql_using_slony.md
new file mode 100644
index 0000000000000000000000000000000000000000..f009906256e4ed5a68239bf01bfbe6a6e414b438
--- /dev/null
+++ b/doc/update/upgrading_postgresql_using_slony.md
@@ -0,0 +1,482 @@
+# Upgrading PostgreSQL Using Slony
+
+This guide describes the steps one can take to upgrade their PostgreSQL database
+to the latest version without the need for hours of downtime. This guide assumes
+you have two database servers: one database server running an older version of
+PostgreSQL (e.g. 9.2.18) and one server running a newer version (e.g. 9.6.0).
+
+For this process we'll use a PostgreSQL replication tool called
+["Slony"](http://www.slony.info/). Slony allows replication between different
+PostgreSQL versions and as such can be used to upgrade a cluster with a minimal
+amount of downtime.
+
+In various places we'll refer to the user `gitlab-psql`. This user should be the
+user used to run the various PostgreSQL OS processes. If you're using a
+different user (e.g. `postgres`) you should replace `gitlab-psql` with the name
+of said user. This guide also assumes your database is called
+`gitlabhq_production`. If you happen to use a different database name you should
+change this accordingly.
+
+## Database Dumps
+
+Slony only replicates data and not any schema changes. As a result we must
+ensure that all databases have the same database structure.
+
+To do so we'll generate a dump of our current database. This dump will only
+contain the structure, not any data. To generate this dump run the following
+command on your active database server:
+
+```bash
+sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql -p 5432 -U gitlab-psql -s -f /tmp/structure.sql gitlabhq_production
+```
+
+If you're not using GitLab's Omnibus package you may have to adjust the paths to
+`pg_dump` and the PostgreSQL installation directory to match the paths of your
+configuration.
+
+Once the structure dump is generated we also need to generate a dump for the
+`schema_migrations` table. This table doesn't have any primary keys and as such
+can't be replicated easily by Slony. To generate this dump run the following
+command on your active database server:
+
+```bash
+sudo -u gitlab-psql /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql/ -p 5432 -U gitlab-psql -a -t schema_migrations -f /tmp/migrations.sql gitlabhq_production
+```
+
+Next we'll need to move these files somewhere accessible by the new database
+server. The easiest way is to simply download these files to your local system:
+
+```bash
+scp your-user@production-database-host:/tmp/*.sql /tmp
+```
+
+This will copy all the SQL files located in `/tmp` to your local system's
+`/tmp` directory. Once copied you can safely remove the files from the database
+server.
+
+## Installing Slony
+
+Slony will be used to upgrade the database without requiring long downtimes.
+Slony can be downloaded from http://www.slony.info/. If you have installed
+PostgreSQL using your operating system's package manager you may also be able to
+install Slony using said package manager.
+
+When compiling Slony from source you *must* use the following commands to do so:
+
+```bash
+./configure --prefix=/path/to/installation/directory --with-perltools --with-pgconfigdir=/path/to/directory/containing/pg_config/bin
+make
+make install
+```
+
+Omnibus users can use the following commands:
+
+```bash
+./configure --prefix=/opt/gitlab/embedded --with-perltools --with-pgconfigdir=/opt/gitlab/embedded/bin
+make
+make install
+```
+
+This assumes you have installed GitLab into /opt/gitlab.
+
+To test if Slony is installed properly, run the following commands:
+
+```bash
+test -f /opt/gitlab/embedded/bin/slonik && echo 'Slony installed' || echo 'Slony not installed'
+test -f /opt/gitlab/embedded/bin/slonik_init_cluster && echo 'Slony Perl tools are available' || echo 'Slony Perl tools are not available'
+/opt/gitlab/embedded/bin/slonik -v
+```
+
+This assumes Slony was installed to `/opt/gitlab/embedded`. If Slony was
+installed properly the output of these commands will be (the mentioned "slonik"
+version may be different):
+
+```
+Slony installed
+Slony Perl tools are available
+slonik version 2.2.5
+```
+
+## Slony User
+
+Next we must set up a PostgreSQL user that Slony can use to replicate your
+database. To do so, log in to your production database using `psql` using a
+super user account. Once done run the following SQL queries:
+
+```sql
+CREATE ROLE slony WITH SUPERUSER LOGIN REPLICATION ENCRYPTED PASSWORD 'password string here';
+ALTER ROLE slony SET statement_timeout TO 0;
+```
+
+Make sure you replace "password string here" with the actual password for the
+user. A password is *required*. This user must be created on _both_ the old and
+new database server using the same password.
+
+Once the user has been created make sure you note down the password as we will
+need it later on.
+
+## Configuring Slony
+
+Now we can finally start configuring Slony. Slony uses a configuration file for
+most of the work so we'll need to set this one up. This configuration file
+specifies where to put log files, how Slony should connect to the databases,
+etc.
+
+First we'll need to create some required directories and set the correct
+permissions. To do so, run the following commands on both the old and new
+database server:
+
+```bash
+sudo mkdir -p /var/log/gitlab/slony /var/run/slony1 /var/opt/gitlab/postgresql/slony
+sudo chown gitlab-psql:root /var/log/gitlab/slony /var/run/slony1 /var/opt/gitlab/postgresql/slony
+```
+
+Here `gitlab-psql` is the user used to run the PostgreSQL database processes. If
+you're using a different user you should replace this with the name of said
+user.
+
+Now that the directories are in place we can create the configuration file. For
+this we can use the following template:
+
+```perl
+if ($ENV{"SLONYNODES"}) {
+    require $ENV{"SLONYNODES"};
+} else {
+    $CLUSTER_NAME = 'slony_replication';
+    $LOGDIR = '/var/log/gitlab/slony';
+    $MASTERNODE = 1;
+    $DEBUGLEVEL = 2;
+
+    add_node(host => 'OLD_HOST', dbname => 'gitlabhq_production', port =>5432,
+        user=>'slony', password=>'SLONY_PASSWORD', node=>1);
+
+    add_node(host => 'NEW_HOST', dbname => 'gitlabhq_production', port =>5432,
+        user=>'slony', password=>'SLONY_PASSWORD', node=>2, parent=>1 );
+}
+
+$SLONY_SETS = {
+    "set1" => {
+        "set_id"       => 1,
+        "table_id"     => 1,
+        "sequence_id"  => 1,
+        "pkeyedtables" => [
+            TABLES
+        ],
+    },
+};
+
+if ($ENV{"SLONYSET"}) {
+    require $ENV{"SLONYSET"};
+}
+
+# Please do not add or change anything below this point.
+1;
+```
+
+In this configuration file you should replace a few placeholders before you can
+use it. The following placeholders should be replaced:
+
+* `OLD_HOST`: the address of the old database server.
+* `NEW_HOST`: the address of the new database server.
+* `SLONY_PASSWORD`: the password of the Slony user created earlier.
+* `TABLES`: the tables to replicate.
+
+The list of tables to replicate can be generated by running the following
+command on your old PostgreSQL database:
+
+```
+sudo gitlab-psql gitlabhq_production -c "select concat('\"', schemaname, '.', tablename, '\",') from pg_catalog.pg_tables where schemaname = 'public' and tableowner = 'gitlab' and tablename != 'schema_migrations' order by tablename asc;" -t
+```
+
+If you're not using Omnibus you should replace `gitlab-psql` with the
+appropriate path to the `psql` executable.
+
+The above command outputs a list of tables in a format that can be copy-pasted
+directly into the above configuration file. Make sure to _replace_ `TABLES` with
+this output, don't just append it below it. Once done you'll end up with
+something like this:
+
+```perl
+"pkeyedtables" => [
+    "public.abuse_reports",
+    "public.appearances",
+    "public.application_settings",
+    ... more rows here ...
+]
+```
+
+Once you have the configuration file generated you must install it on both the
+old and new database. To do so, place it in
+`/var/opt/gitlab/postgresql/slony/slon_tools.conf` (for which we created the
+directory earlier on).
+
+Now that the configuration file is in place we can _finally_ start replicating
+our database. First we must set up the schema in our new database. To do so make
+sure that the SQL files we generated earlier can be found in the `/tmp`
+directory of the new server. Once these files are in place start a `psql`
+session on this server:
+
+```
+sudo gitlab-psql gitlabhq_production
+```
+
+Now run the following commands:
+
+```
+\i /tmp/structure.sql
+\i /tmp/migrations.sql
+```
+
+To verify if the structure is in place close the session, start it again, then
+run `\d`. If all went well you should see output along the lines of the
+following:
+
+```
+                               List of relations
+ Schema |                    Name                     |   Type   |    Owner
+--------+---------------------------------------------+----------+-------------
+ public | abuse_reports                               | table    | gitlab
+ public | abuse_reports_id_seq                        | sequence | gitlab
+ public | appearances                                 | table    | gitlab
+ public | appearances_id_seq                          | sequence | gitlab
+ public | application_settings                        | table    | gitlab
+ public | application_settings_id_seq                 | sequence | gitlab
+ public | approvals                                   | table    | gitlab
+ ... more rows here ...
+```
+
+Now we can initialize the required tables and what not that Slony will use for
+its replication process. To do so, run the following on the old database:
+
+```
+sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_init_cluster --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
+```
+
+If all went well this will produce something along the lines of:
+
+```
+<stdin>:10: Set up replication nodes
+<stdin>:13: Next: configure paths for each node/origin
+<stdin>:16: Replication nodes prepared
+<stdin>:17: Please start a slon replication daemon for each node
+```
+
+Next we need to start a replication node on every server. To do so, run the
+following on the old database:
+
+```
+sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 1 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
+```
+
+If all went well this will produce output such as:
+
+
+```
+Invoke slon for node 1 - /opt/gitlab/embedded/bin/slon -p /var/run/slony1/slony_replication_node1.pid -s 1000 -d2  slony_replication 'host=192.168.0.7 dbname=gitlabhq_production user=slony port=5432 password=hieng8ezohHuCeiqu0leeghai4aeyahp' > /var/log/gitlab/slony/node1/gitlabhq_production-2016-10-06.log 2>&1 &
+Slon successfully started for cluster slony_replication, node node1
+PID [26740]
+Start the watchdog process as well...
+```
+
+Next we need to run the following command on the _new_ database server:
+
+```
+sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_start 2 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
+```
+
+This will produce similar output if all went well.
+
+Next we need to tell the new database server what it should replicate. This can
+be done by running the following command on the _new_ database server:
+
+```
+sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_create_set 1 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
+```
+
+This should produce output along the lines of the following:
+
+```
+<stdin>:11: Subscription set 1 (set1) created
+<stdin>:12: Adding tables to the subscription set
+<stdin>:16: Add primary keyed table public.abuse_reports
+<stdin>:20: Add primary keyed table public.appearances
+<stdin>:24: Add primary keyed table public.application_settings
+... more rows here ...
+<stdin>:327: Adding sequences to the subscription set
+<stdin>:328: All tables added
+```
+
+Finally we can start the replication process by running the following on the
+_new_ database server:
+
+```
+sudo -u gitlab-psql /opt/gitlab/embedded/bin/slonik_subscribe_set 1 2 --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf | /opt/gitlab/embedded/bin/slonik
+```
+
+This should produce the following output:
+
+```
+<stdin>:6: Subscribed nodes to set 1
+```
+
+At this point the new database server will start replicating the data of the old
+database server. This process can take anywhere from a few minutes to hours, if
+not days. Unfortunately Slony itself doesn't really provide a way of knowing
+when the two databases are in sync. To get an estimate of the progress you can
+use the following shell script:
+
+```
+#!/usr/bin/env bash
+
+set -e
+
+user='slony'
+pass='SLONY_PASSWORD'
+
+function main {
+    while :
+    do
+        local source
+        local target
+
+        source=$(PGUSER="${user}" PGPASSWORD="${pass}" /opt/gitlab/embedded/bin/psql -h OLD_HOST gitlabhq_production -c "select pg_size_pretty(pg_database_size('gitlabhq_production'));" -t -A)
+        target=$(PGUSER="${user}" PGPASSWORD="${pass}" /opt/gitlab/embedded/bin/psql -h NEW_HOST gitlabhq_production -c "select pg_size_pretty(pg_database_size('gitlabhq_production'));" -t -A)
+
+        echo "$(date): ${target} of ${source}" >> progress.log
+        echo "$(date): ${target} of ${source}"
+
+        sleep 60
+    done
+}
+
+main
+```
+
+This script will compare the sizes of the old and new database every minute and
+print the result to STDOUT as well as logging it to a file. Make sure to replace
+`SLONY_PASSWORD`, `OLD_HOST`, and `NEW_HOST` with the correct values.
+
+## Stopping Replication
+
+At some point the two databases are in sync. Once this is the case you'll need
+to plan for a few minutes of downtime. This small downtime window is used to
+stop the replication process, remove any Slony data from both databases, restart
+GitLab so it can use the new database, etc.
+
+First, let's stop all of GitLab. Omnibus users can do so by running the
+following on their GitLab server(s):
+
+```
+sudo gitlab-ctl stop unicorn
+sudo gitlab-ctl stop sidekiq
+sudo gitlab-ctl stop mailroom
+```
+
+If you have any other processes that use PostgreSQL you should also stop those.
+
+Once everything has been stopped you should update any configuration settings,
+DNS records, etc so they all point to the new database.
+
+Once the settings have been taken care of we need to stop the replication
+process. It's crucial that no new data is written to the databases at this point
+as this data will be lost.
+
+To stop replication, run the following on both database servers:
+
+```bash
+sudo -u gitlab-psql /opt/gitlab/embedded/bin/slon_kill --conf /var/opt/gitlab/postgresql/slony/slon_tools.conf
+```
+
+This will stop all the Slony processes on the host the command was executed on.
+
+## Resetting Sequences
+
+The above setup does not replicate database sequences, as such these must be
+reset manually in the target database. You can use the following script for
+this:
+
+```bash
+#!/usr/bin/env bash
+set -e
+
+function main {
+    local fix_sequences
+    local fix_owners
+
+    fix_sequences='/tmp/fix_sequences.sql'
+    fix_owners='/tmp/fix_owners.sql'
+
+    # The SQL queries were taken from
+    # https://wiki.postgresql.org/wiki/Fixing_Sequences
+    sudo gitlab-psql gitlabhq_production -t -c "
+    SELECT 'ALTER SEQUENCE '|| quote_ident(MIN(schema_name)) ||'.'|| quote_ident(MIN(seq_name))
+           ||' OWNED BY '|| quote_ident(MIN(TABLE_NAME)) ||'.'|| quote_ident(MIN(column_name)) ||';'
+    FROM (
+        SELECT
+            n.nspname AS schema_name,
+            c.relname AS TABLE_NAME,
+            a.attname AS column_name,
+            SUBSTRING(d.adsrc FROM E'^nextval\\(''([^'']*)''(?:::text|::regclass)?\\)') AS seq_name
+        FROM pg_class c
+        JOIN pg_attribute a ON (c.oid=a.attrelid)
+        JOIN pg_attrdef d ON (a.attrelid=d.adrelid AND a.attnum=d.adnum)
+        JOIN pg_namespace n ON (c.relnamespace=n.oid)
+        WHERE has_schema_privilege(n.oid,'USAGE')
+          AND n.nspname NOT LIKE 'pg!_%' escape '!'
+          AND has_table_privilege(c.oid,'SELECT')
+          AND (NOT a.attisdropped)
+          AND d.adsrc ~ '^nextval'
+    ) seq
+    GROUP BY seq_name HAVING COUNT(*)=1;
+    " > "${fix_owners}"
+
+    sudo gitlab-psql gitlabhq_production -t -c "
+    SELECT 'SELECT SETVAL(' ||
+           quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
+           ', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
+           quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
+    FROM pg_class AS S,
+         pg_depend AS D,
+         pg_class AS T,
+         pg_attribute AS C,
+         pg_tables AS PGT
+    WHERE S.relkind = 'S'
+        AND S.oid = D.objid
+        AND D.refobjid = T.oid
+        AND D.refobjid = C.attrelid
+        AND D.refobjsubid = C.attnum
+        AND T.relname = PGT.tablename
+    ORDER BY S.relname;
+    " > "${fix_sequences}"
+
+    sudo gitlab-psql gitlabhq_production -f "${fix_owners}"
+    sudo gitlab-psql gitlabhq_production -f "${fix_sequences}"
+
+    rm "${fix_owners}" "${fix_sequences}"
+}
+
+main
+```
+
+Upload this script to the _target_ server and execute it as follows:
+
+```bash
+bash path/to/the/script/above.sh
+```
+
+This will correct the ownership of sequences and reset the next value for the
+`id` column to the next available value.
+
+## Removing Slony
+
+Next we need to remove all Slony related data. To do so, run the following
+command on the _target_ server:
+
+```bash
+sudo gitlab-psql gitlabhq_production -c "DROP SCHEMA _slony_replication CASCADE;"
+```
+
+Once done you can safely remove any Slony related files (e.g. the log
+directory), and uninstall Slony if desired. At this point you can start your
+GitLab instance again and if all went well it should be using your new database
+server.
diff --git a/features/dashboard/active_tab.feature b/features/dashboard/active_tab.feature
index 08b87808f337eac54d8b70809af9b78a532b5f5b..bd883a0ebfafb09e6838fd4968cbb26eba43f34d 100644
--- a/features/dashboard/active_tab.feature
+++ b/features/dashboard/active_tab.feature
@@ -18,7 +18,7 @@ Feature: Dashboard Active Tab
     Then the active main tab should be Merge Requests
     And no other main tabs should be active
 
-  Scenario: On Dashboard Help
-    Given I visit dashboard help page
-    Then the active main tab should be Help
+  Scenario: On Dashboard Groups
+    Given I visit dashboard groups page
+    Then the active main tab should be Groups
     And no other main tabs should be active
diff --git a/features/dashboard/dashboard.feature b/features/dashboard/dashboard.feature
index b1d5e4a7acbf707a3625b49ab938ee8143d29a69..92061dac7f446ba25839cbe791c136b74be935f8 100644
--- a/features/dashboard/dashboard.feature
+++ b/features/dashboard/dashboard.feature
@@ -11,7 +11,6 @@ Feature: Dashboard
     And I visit dashboard page
 
   Scenario: I should see projects list
-    Then I should see "New Project" link
     Then I should see "Shop" project link
     Then I should see "Shop" project CI status
 
diff --git a/features/steps/admin/logs.rb b/features/steps/admin/logs.rb
index f9e49588c7541c9ffddba14b2171ce70201d3a27..63881d69146fb708e08680fa4b212d44f1cd3cae 100644
--- a/features/steps/admin/logs.rb
+++ b/features/steps/admin/logs.rb
@@ -4,7 +4,7 @@ class Spinach::Features::AdminLogs < Spinach::FeatureSteps
   include SharedAdmin
 
   step 'I should see tabs with available logs' do
-    expect(page).to have_content 'production.log'
+    expect(page).to have_content 'test.log'
     expect(page).to have_content 'githost.log'
     expect(page).to have_content 'application.log'
   end
diff --git a/features/steps/dashboard/help.rb b/features/steps/dashboard/help.rb
index 9c94dc70df0bda915ec42b9d9e3cd0366c265dff..3c5bf44c538bb722bd64335f790412b7702f9ba3 100644
--- a/features/steps/dashboard/help.rb
+++ b/features/steps/dashboard/help.rb
@@ -8,7 +8,7 @@ class Spinach::Features::DashboardHelp < Spinach::FeatureSteps
   end
 
   step 'I visit the "Rake Tasks" help page' do
-    visit help_page_path("raketasks/maintenance")
+    visit help_page_path("administration/raketasks/maintenance")
   end
 
   step 'I should see "Rake Tasks" page markdown rendered' do
diff --git a/features/steps/shared/sidebar_active_tab.rb b/features/steps/shared/sidebar_active_tab.rb
index 5c47238777fd77113aa51d2d7b150185afe19ed0..07fff16e867fe0f985b46e5629e6a10d1c3ff6a8 100644
--- a/features/steps/shared/sidebar_active_tab.rb
+++ b/features/steps/shared/sidebar_active_tab.rb
@@ -1,12 +1,8 @@
 module SharedSidebarActiveTab
   include Spinach::DSL
 
-  step 'the active main tab should be Help' do
-    ensure_active_main_tab('Help')
-  end
-
   step 'no other main tabs should be active' do
-    expect(page).to have_selector('.nav-sidebar > li.active', count: 1)
+    expect(page).to have_selector('.nav-sidebar li.active', count: 1)
   end
 
   def ensure_active_main_tab(content)
@@ -17,6 +13,10 @@ module SharedSidebarActiveTab
     ensure_active_main_tab('Projects')
   end
 
+  step 'the active main tab should be Groups' do
+    ensure_active_main_tab('Groups')
+  end
+
   step 'the active main tab should be Projects' do
     ensure_active_main_tab('Projects')
   end
@@ -28,8 +28,4 @@ module SharedSidebarActiveTab
   step 'the active main tab should be Merge Requests' do
     ensure_active_main_tab('Merge Requests')
   end
-
-  step 'the active main tab should be Help' do
-    ensure_active_main_tab('Help')
-  end
 end
diff --git a/generator_templates/rails/post_deployment_migration/migration.rb b/generator_templates/rails/post_deployment_migration/migration.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1a7b8d5bf3510616d80db6a5d473d81838b16e1c
--- /dev/null
+++ b/generator_templates/rails/post_deployment_migration/migration.rb
@@ -0,0 +1,22 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class <%= migration_class_name %> < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  DOWNTIME = false
+
+  # When using the methods "add_concurrent_index" or "add_column_with_default"
+  # you must disable the use of transactions as these methods can not run in an
+  # existing transaction. When using "add_concurrent_index" make sure that this
+  # method is the _only_ method called in the migration, any other changes
+  # should go in a separate migration. This ensures that upon failure _only_ the
+  # index creation fails and can be retried or reverted easily.
+  #
+  # To disable transactions uncomment the following line and remove these
+  # comments:
+  # disable_ddl_transaction!
+
+  def change
+  end
+end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index ab9d2d54f4b86cc528555f8140e4285055122ac5..1f378ba163547cebf5b2d3a5c0a1a5045a455bec 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -43,14 +43,13 @@ module API
     end
 
     class Hook < Grape::Entity
-      expose :id, :url, :created_at
+      expose :id, :url, :created_at, :push_events, :tag_push_events
+      expose :enable_ssl_verification
     end
 
     class ProjectHook < Hook
-      expose :project_id, :push_events
-      expose :issues_events, :merge_requests_events, :tag_push_events
+      expose :project_id, :issues_events, :merge_requests_events
       expose :note_events, :build_events, :pipeline_events, :wiki_page_events
-      expose :enable_ssl_verification
     end
 
     class BasicProjectDetails < Grape::Entity
@@ -510,6 +509,7 @@ module API
       expose :after_sign_out_path
       expose :container_registry_token_expire_delay
       expose :repository_storage
+      expose :repository_storages
       expose :koding_enabled
       expose :koding_url
     end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index bfb8947502538da798550ab5d74ebc43fef35f41..a13e353b7f5e41cceade79946bb195c255c8c358 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -8,11 +8,14 @@ module API
       #
       # Parameters:
       #   skip_groups (optional) - Array of group ids to exclude from list
+      #   all_available (optional, boolean) - Show all group that you have access to
       # Example Request:
       #  GET /groups
       get do
         @groups = if current_user.admin
                     Group.all
+                  elsif params[:all_available]
+                    GroupsFinder.new.execute(current_user)
                   else
                     current_user.groups
                   end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 8025581d3ca3eac50a96b74a2580d0b34a464ebe..3c9d7b1aaef9fad3113649225b15bffe0ca8349c 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -1,18 +1,12 @@
 module API
   module Helpers
+    include Gitlab::Utils
+
     PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
     PRIVATE_TOKEN_PARAM = :private_token
     SUDO_HEADER = "HTTP_SUDO"
     SUDO_PARAM = :sudo
 
-    def to_boolean(value)
-      return value if [true, false].include?(value)
-      return true if value =~ /^(true|t|yes|y|1|on)$/i
-      return false if value =~ /^(false|f|no|n|0|off)$/i
-
-      nil
-    end
-
     def private_token
       params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]
     end
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 14f5be3b5f64bef5a0cc2e8bfba22eda9c276863..dd93a85dc5427c1147ca135f089e063f1d58906e 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -47,7 +47,8 @@ module API
           :build_events,
           :pipeline_events,
           :wiki_page_events,
-          :enable_ssl_verification
+          :enable_ssl_verification,
+          :token
         ]
         @hook = user_project.hooks.new(attrs)
 
@@ -82,7 +83,8 @@ module API
           :build_events,
           :pipeline_events,
           :wiki_page_events,
-          :enable_ssl_verification
+          :enable_ssl_verification,
+          :token
         ]
 
         if @hook.update_attributes attrs
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index c885fcd7ea34abe2d8417b7e8fc0eb62e27a8545..c4cb1c7924ab534fdd6b16f49a7ff2b794f13fdd 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -17,12 +17,12 @@ module API
       present current_settings, with: Entities::ApplicationSetting
     end
 
-    # Modify applicaiton settings
+    # Modify application settings
     #
     # Example Request:
     #   PUT /application/settings
     put "application/settings" do
-      attributes = current_settings.attributes.keys - ["id"]
+      attributes = ["repository_storage"] + current_settings.attributes.keys - ["id"]
       attrs = attributes_for_keys(attributes)
 
       if current_settings.update_attributes(attrs)
diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb
index 794e34874f4de435aa73a83d182140359c5bbd59..32f731c565221eaf2740548345dbb5a5445eec7e 100644
--- a/lib/api/system_hooks.rb
+++ b/lib/api/system_hooks.rb
@@ -12,6 +12,7 @@ module API
       end
       get do
         hooks = SystemHook.all
+
         present hooks, with: Entities::Hook
       end
 
@@ -19,10 +20,14 @@ module API
         success Entities::Hook
       end
       params do
-        requires :url, type: String, desc: 'The URL for the system hook'
+        requires :url, type: String, desc: "The URL to send the request to"
+        optional :token, type: String, desc: 'The token used to validate payloads'
+        optional :push_events, type: Boolean, desc: "Trigger hook on push events"
+        optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events"
+        optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
       end
       post do
-        hook = SystemHook.new declared(params).to_h
+        hook = SystemHook.new declared(params, include_missing: false).to_h
 
         if hook.save
           present hook, with: Entities::Hook
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index cb213a76a054a1cb9bff4ecfbf260a47ea1a924d..3740d4fb4cd598b047d661126c42d66fbad6114a 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -102,10 +102,10 @@ module Banzai
             end
 
           elsif element_node?(node)
-            yield_valid_link(node) do |link, text|
+            yield_valid_link(node) do |link, inner_html|
               if ref_pattern && link =~ /\A#{ref_pattern}\z/
                 replace_link_node_with_href(node, link) do
-                  object_link_filter(link, ref_pattern, link_text: text)
+                  object_link_filter(link, ref_pattern, link_content: inner_html)
                 end
 
                 next
@@ -113,9 +113,9 @@ module Banzai
 
               next unless link_pattern
 
-              if link == text && text =~ /\A#{link_pattern}/
+              if link == inner_html && inner_html =~ /\A#{link_pattern}/
                 replace_link_node_with_text(node, link) do
-                  object_link_filter(text, link_pattern)
+                  object_link_filter(inner_html, link_pattern)
                 end
 
                 next
@@ -123,7 +123,7 @@ module Banzai
 
               if link =~ /\A#{link_pattern}\z/
                 replace_link_node_with_href(node, link) do
-                  object_link_filter(link, link_pattern, link_text: text)
+                  object_link_filter(link, link_pattern, link_content: inner_html)
                 end
 
                 next
@@ -140,11 +140,11 @@ module Banzai
       #
       # text - String text to replace references in.
       # pattern - Reference pattern to match against.
-      # link_text - Original content of the link being replaced.
+      # link_content - Original content of the link being replaced.
       #
       # Returns a String with references replaced with links. All links
       # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
-      def object_link_filter(text, pattern, link_text: nil)
+      def object_link_filter(text, pattern, link_content: nil)
         references_in(text, pattern) do |match, id, project_ref, matches|
           project = project_from_ref_cached(project_ref)
 
@@ -152,7 +152,7 @@ module Banzai
             title = object_link_title(object)
             klass = reference_class(object_sym)
 
-            data = data_attributes_for(link_text || match, project, object)
+            data = data_attributes_for(link_content || match, project, object)
 
             if matches.names.include?("url") && matches[:url]
               url = matches[:url]
@@ -160,11 +160,11 @@ module Banzai
               url = url_for_object_cached(object, project)
             end
 
-            text = link_text || object_link_text(object, matches)
+            content = link_content || object_link_text(object, matches)
 
             %(<a href="#{url}" #{data}
                  title="#{escape_once(title)}"
-                 class="#{klass}">#{escape_once(text)}</a>)
+                 class="#{klass}">#{content}</a>)
           else
             match
           end
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index 0d20be557a04f989f2c19856a1c77e6ae9f1e027..dce4de3ceaf800acb5dc6d1237f8646796046d70 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -37,10 +37,10 @@ module Banzai
             end
 
           elsif element_node?(node)
-            yield_valid_link(node) do |link, text|
+            yield_valid_link(node) do |link, inner_html|
               if link =~ ref_start_pattern
                 replace_link_node_with_href(node, link) do
-                  issue_link_filter(link, link_text: text)
+                  issue_link_filter(link, link_content: inner_html)
                 end
               end
             end
@@ -54,10 +54,11 @@ module Banzai
       # issue's details page.
       #
       # text - String text to replace references in.
+      # link_content - Original content of the link being replaced.
       #
       # Returns a String with `JIRA-123` references replaced with links. All
       # links have `gfm` and `gfm-issue` class names attached for styling.
-      def issue_link_filter(text, link_text: nil)
+      def issue_link_filter(text, link_content: nil)
         project = context[:project]
 
         self.class.references_in(text, issue_reference_pattern) do |match, id|
@@ -69,11 +70,11 @@ module Banzai
           klass = reference_class(:issue)
           data  = data_attribute(project: project.id, external_issue: id)
 
-          text = link_text || match
+          content = link_content || match
 
           %(<a href="#{url}" #{data}
                title="#{escape_once(title)}"
-               class="#{klass}">#{escape_once(text)}</a>)
+               class="#{klass}">#{content}</a>)
         end
       end
 
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 2d221290f7eb0e08c3cf01145ebb5e7742173890..84bfeac80417a21ef7fcbcec13f9b1a5bfaafdf3 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -85,14 +85,14 @@ module Banzai
         @nodes ||= each_node.to_a
       end
 
-      # Yields the link's URL and text whenever the node is a valid <a> tag.
+      # Yields the link's URL and inner HTML whenever the node is a valid <a> tag.
       def yield_valid_link(node)
         link = CGI.unescape(node.attr('href').to_s)
-        text = node.text
+        inner_html = node.inner_html
 
         return unless link.force_encoding('UTF-8').valid_encoding?
 
-        yield link, text
+        yield link, inner_html
       end
 
       def replace_text_when_pattern_matches(node, pattern)
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index c6302b586d31358efb8b24c06bab67bcc833445a..f842b1fb779d7b79a9ade9f80814b7eb54ed3847 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -35,10 +35,10 @@ module Banzai
               user_link_filter(content)
             end
           elsif element_node?(node)
-            yield_valid_link(node) do |link, text|
+            yield_valid_link(node) do |link, inner_html|
               if link =~ ref_pattern_start
                 replace_link_node_with_href(node, link) do
-                  user_link_filter(link, link_text: text)
+                  user_link_filter(link, link_content: inner_html)
                 end
               end
             end
@@ -52,15 +52,16 @@ module Banzai
       # user's profile page.
       #
       # text - String text to replace references in.
+      # link_content - Original content of the link being replaced.
       #
       # Returns a String with `@user` references replaced with links. All links
       # have `gfm` and `gfm-project_member` class names attached for styling.
-      def user_link_filter(text, link_text: nil)
+      def user_link_filter(text, link_content: nil)
         self.class.references_in(text) do |match, username|
           if username == 'all'
-            link_to_all(link_text: link_text)
+            link_to_all(link_content: link_content)
           elsif namespace = namespaces[username]
-            link_to_namespace(namespace, link_text: link_text) || match
+            link_to_namespace(namespace, link_content: link_content) || match
           else
             match
           end
@@ -102,49 +103,49 @@ module Banzai
         reference_class(:project_member)
       end
 
-      def link_to_all(link_text: nil)
+      def link_to_all(link_content: nil)
         project = context[:project]
         author = context[:author]
 
         if author && !project.team.member?(author)
-          link_text
+          link_content
         else
           url = urls.namespace_project_url(project.namespace, project,
                                            only_path: context[:only_path])
 
           data = data_attribute(project: project.id, author: author.try(:id))
-          text = link_text || User.reference_prefix + 'all'
+          content = link_content || User.reference_prefix + 'all'
 
-          link_tag(url, data, text, 'All Project and Group Members')
+          link_tag(url, data, content, 'All Project and Group Members')
         end
       end
 
-      def link_to_namespace(namespace, link_text: nil)
+      def link_to_namespace(namespace, link_content: nil)
         if namespace.is_a?(Group)
-          link_to_group(namespace.path, namespace, link_text: link_text)
+          link_to_group(namespace.path, namespace, link_content: link_content)
         else
-          link_to_user(namespace.path, namespace, link_text: link_text)
+          link_to_user(namespace.path, namespace, link_content: link_content)
         end
       end
 
-      def link_to_group(group, namespace, link_text: nil)
+      def link_to_group(group, namespace, link_content: nil)
         url = urls.group_url(group, only_path: context[:only_path])
         data = data_attribute(group: namespace.id)
-        text = link_text || Group.reference_prefix + group
+        content = link_content || Group.reference_prefix + group
 
-        link_tag(url, data, text, namespace.name)
+        link_tag(url, data, content, namespace.name)
       end
 
-      def link_to_user(user, namespace, link_text: nil)
+      def link_to_user(user, namespace, link_content: nil)
         url = urls.user_url(user, only_path: context[:only_path])
         data = data_attribute(user: namespace.owner_id)
-        text = link_text || User.reference_prefix + user
+        content = link_content || User.reference_prefix + user
 
-        link_tag(url, data, text, namespace.owner_name)
+        link_tag(url, data, content, namespace.owner_name)
       end
 
-      def link_tag(url, data, text, title)
-        %(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{escape_once(text)}</a>)
+      def link_tag(url, data, link_content, title)
+        %(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{link_content}</a>)
       end
     end
   end
diff --git a/lib/banzai/redactor.rb b/lib/banzai/redactor.rb
index 0df3a72d1c46d3395ea7d9744cf5c93a5c4d004a..de3ebe72720e698849ef1a8d99bdf49e374b6cc5 100644
--- a/lib/banzai/redactor.rb
+++ b/lib/banzai/redactor.rb
@@ -41,10 +41,10 @@ module Banzai
           next if visible.include?(node)
 
           doc_data[:visible_reference_count] -= 1
-          # The reference should be replaced by the original text,
-          # which is not always the same as the rendered text.
-          text = node.attr('data-original') || node.text
-          node.replace(text)
+          # The reference should be replaced by the original link's content,
+          # which is not always the same as the rendered one.
+          content = node.attr('data-original') || node.inner_html
+          node.replace(content)
         end
       end
 
diff --git a/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7cb4bccb23c4c1b037da3b9b7093e3a22a6ce774
--- /dev/null
+++ b/lib/generators/rails/post_deployment_migration/post_deployment_migration_generator.rb
@@ -0,0 +1,15 @@
+require 'rails/generators'
+
+module Rails
+  class PostDeploymentMigrationGenerator < Rails::Generators::NamedBase
+    def create_migration_file
+      timestamp = Time.now.strftime('%Y%m%d%H%I%S')
+
+      template "migration.rb", "db/post_migrate/#{timestamp}_#{file_name}.rb"
+    end
+
+    def migration_class_name
+      file_name.camelize
+    end
+  end
+end
diff --git a/lib/gitlab/production_logger.rb b/lib/gitlab/environment_logger.rb
similarity index 50%
rename from lib/gitlab/production_logger.rb
rename to lib/gitlab/environment_logger.rb
index 89ce7144b1bce57881ac850ee8cbfbfdcc250adb..407cc572656614ea323eac43dee31b95a95a1a26 100644
--- a/lib/gitlab/production_logger.rb
+++ b/lib/gitlab/environment_logger.rb
@@ -1,7 +1,7 @@
 module Gitlab
-  class ProductionLogger < Gitlab::Logger
+  class EnvironmentLogger < Gitlab::Logger
     def self.file_name_noext
-      'production'
+      Rails.env
     end
   end
 end
diff --git a/lib/gitlab/import_export/attribute_cleaner.rb b/lib/gitlab/import_export/attribute_cleaner.rb
index f755a40469313faad06d2b507274d290e5234471..34169319b263cf115c6116d2cd8f935985f9fcac 100644
--- a/lib/gitlab/import_export/attribute_cleaner.rb
+++ b/lib/gitlab/import_export/attribute_cleaner.rb
@@ -3,10 +3,25 @@ module Gitlab
     class AttributeCleaner
       ALLOWED_REFERENCES = RelationFactory::PROJECT_REFERENCES + RelationFactory::USER_REFERENCES + ['group_id']
 
-      def self.clean!(relation_hash:)
-        relation_hash.reject! do |key, _value|
-          key.end_with?('_id') && !ALLOWED_REFERENCES.include?(key)
-        end
+      def self.clean(*args)
+        new(*args).clean
+      end
+
+      def initialize(relation_hash:, relation_class:)
+        @relation_hash = relation_hash
+        @relation_class = relation_class
+      end
+
+      def clean
+        @relation_hash.reject do |key, _value|
+          prohibited_key?(key) || !@relation_class.attribute_method?(key)
+        end.except('id')
+      end
+
+      private
+
+      def prohibited_key?(key)
+        key.end_with?('_id') && !ALLOWED_REFERENCES.include?(key)
       end
     end
   end
diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb
index 113895ba22c96aad98c5a5abbf605438b28ca6c7..ffd17118c91a2112b41f011db6fc2289d6e0cb59 100644
--- a/lib/gitlab/import_export/file_importer.rb
+++ b/lib/gitlab/import_export/file_importer.rb
@@ -43,6 +43,14 @@ module Gitlab
 
         raise Projects::ImportService::Error.new("Unable to decompress #{@archive_file} into #{@shared.export_path}") unless result
 
+        remove_symlinks!
+      end
+
+      def remove_symlinks!
+        Dir["#{@shared.export_path}/**/*"].each do |path|
+          FileUtils.rm(path) if File.lstat(path).symlink?
+        end
+
         true
       end
     end
diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb
index 36c4cf6efa0493deef2d098cdb3f625bf96902e1..b790733f4a75a3bba78db43504aaadd1bd1420bd 100644
--- a/lib/gitlab/import_export/members_mapper.rb
+++ b/lib/gitlab/import_export/members_mapper.rb
@@ -55,7 +55,12 @@ module Gitlab
       end
 
       def member_hash(member)
-        member.except('id').merge(source_id: @project.id, importing: true)
+        parsed_hash(member).merge('source_id' => @project.id, 'importing' => true)
+      end
+
+      def parsed_hash(member)
+        Gitlab::ImportExport::AttributeCleaner.clean(relation_hash: member.deep_stringify_keys,
+                                                     relation_class: ProjectMember)
       end
 
       def find_project_user_query(member)
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index 7cdba880a93f01519dcd785a82742708b8d1432a..c551321c18dbb3aeb879cdc37138e40941bddb70 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -9,8 +9,14 @@ module Gitlab
       end
 
       def restore
-        json = IO.read(@path)
-        @tree_hash = ActiveSupport::JSON.decode(json)
+        begin
+          json = IO.read(@path)
+          @tree_hash = ActiveSupport::JSON.decode(json)
+        rescue => e
+          Rails.logger.error("Import/Export error: #{e.message}")
+          raise Gitlab::ImportExport::Error.new('Incorrect JSON format')
+        end
+
         @project_members = @tree_hash.delete('project_members')
 
         ActiveRecord::Base.no_touching do
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index dc630e76411e3a4d4b48d964686a84f2129bf2c9..a0e80fccad9295c903e2fef142ebecc77251f3e7 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -14,7 +14,7 @@ module Gitlab
                     priorities: :label_priorities,
                     label: :project_label }.freeze
 
-      USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id].freeze
+      USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id].freeze
 
       PROJECT_REFERENCES = %w[project_id source_project_id gl_project_id target_project_id].freeze
 
@@ -30,7 +30,7 @@ module Gitlab
 
       def initialize(relation_sym:, relation_hash:, members_mapper:, user:, project_id:)
         @relation_name = OVERRIDES[relation_sym] || relation_sym
-        @relation_hash = relation_hash.except('id', 'noteable_id').merge('project_id' => project_id)
+        @relation_hash = relation_hash.except('noteable_id').merge('project_id' => project_id)
         @members_mapper = members_mapper
         @user = user
         @imported_object_retries = 0
@@ -172,11 +172,8 @@ module Gitlab
       end
 
       def parsed_relation_hash
-        @parsed_relation_hash ||= begin
-          Gitlab::ImportExport::AttributeCleaner.clean!(relation_hash: @relation_hash)
-
-          @relation_hash.reject { |k, _v| !relation_class.attribute_method?(k) }
-        end
+        @parsed_relation_hash ||= Gitlab::ImportExport::AttributeCleaner.clean(relation_hash: @relation_hash,
+                                                                               relation_class: relation_class)
       end
 
       def set_st_diffs
diff --git a/lib/gitlab/import_export/version_checker.rb b/lib/gitlab/import_export/version_checker.rb
index fc08082fc862cf59ce36037daec1dc3c3ea6b210..bd3c3ee3b2fa8835f2d893a941ef4febc614467e 100644
--- a/lib/gitlab/import_export/version_checker.rb
+++ b/lib/gitlab/import_export/version_checker.rb
@@ -24,12 +24,19 @@ module Gitlab
       end
 
       def verify_version!(version)
-        if Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version)
+        if different_version?(version)
           raise Gitlab::ImportExport::Error.new("Import version mismatch: Required #{Gitlab::ImportExport.version} but was #{version}")
         else
           true
         end
       end
+
+      def different_version?(version)
+        Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version)
+      rescue => e
+        Rails.logger.error("Import/Export error: #{e.message}")
+        raise Gitlab::ImportExport::Error.new('Incorrect VERSION format')
+      end
     end
   end
 end
diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb
index e59ead5d76c6c45cdb91a4c1719073b157d1a9c5..4c395b4266ebb82c5e9ad99d05442830741ed451 100644
--- a/lib/gitlab/utils.rb
+++ b/lib/gitlab/utils.rb
@@ -13,5 +13,13 @@ module Gitlab
     def force_utf8(str)
       str.force_encoding(Encoding::UTF_8)
     end
+
+    def to_boolean(value)
+      return value if [true, false].include?(value)
+      return true if value =~ /^(true|t|yes|y|1|on)$/i
+      return false if value =~ /^(false|f|no|n|0|off)$/i
+
+      nil
+    end
   end
 end
diff --git a/lib/tasks/gitlab/generate_docs.rake b/lib/tasks/gitlab/generate_docs.rake
deleted file mode 100644
index f6448c38e1006b2d7c3c320070d579e68ee59259..0000000000000000000000000000000000000000
--- a/lib/tasks/gitlab/generate_docs.rake
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace :gitlab do
-  desc "GitLab | Generate sdocs for project"
-  task generate_docs: :environment do
-    system(*%W(bundle exec sdoc -o doc/code app lib))
-  end
-end
-
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 210899882b476b7be11aaaaa7f4afd8dc4fdad14..58761a129d42a41fb078d75990413bdee08e1505 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -63,11 +63,11 @@ namespace :gitlab do
 
         # Launch installation process
         system(*%W(bin/install) + repository_storage_paths_args)
-
-        # (Re)create hooks
-        system(*%W(bin/create-hooks) + repository_storage_paths_args)
       end
 
+      # (Re)create hooks
+      Rake::Task['gitlab:shell:create_hooks'].invoke
+
       # Required for debian packaging with PKGR: Setup .ssh/environment with
       # the current PATH, so that the correct ruby version gets loaded
       # Requires to set "PermitUserEnvironment yes" in sshd config (should not
@@ -102,6 +102,15 @@ namespace :gitlab do
         end
       end
     end
+
+    desc 'Create or repair repository hooks symlink'
+    task create_hooks: :environment do
+      warn_user_is_not_gitlab
+
+      puts 'Creating/Repairing hooks symlinks for all repositories'
+      system(*%W(#{Gitlab.config.gitlab_shell.path}/bin/create-hooks) + repository_storage_paths_args)
+      puts 'done'.color(:green)
+    end
   end
 
   def setup
diff --git a/package.json b/package.json
index d440307bd103a822bdadefa4405f738c9685e73e..a303c9c1eac9a7b024be8467568f6994e217e105 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
   "devDependencies": {
     "eslint": "^3.1.1",
     "eslint-config-airbnb": "^12.0.0",
+    "eslint-plugin-filenames": "^1.1.0",
     "eslint-plugin-import": "^2.0.1",
     "eslint-plugin-jsx-a11y": "^2.2.3",
     "eslint-plugin-react": "^6.4.1"
diff --git a/spec/bin/changelog_spec.rb b/spec/bin/changelog_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7f4298db59f77d51c943fc01340911b023a72d98
--- /dev/null
+++ b/spec/bin/changelog_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+load File.expand_path('../../bin/changelog', __dir__)
+
+describe 'bin/changelog' do
+  describe ChangelogOptionParser do
+    it 'parses --ammend' do
+      options = described_class.parse(%w[foo bar --amend])
+
+      expect(options.amend).to eq true
+    end
+
+    it 'parses --force and -f' do
+      %w[--force -f].each do |flag|
+        options = described_class.parse(%W[foo #{flag} bar])
+
+        expect(options.force).to eq true
+      end
+    end
+
+    it 'parses --merge-request and -m' do
+      %w[--merge-request -m].each do |flag|
+        options = described_class.parse(%W[foo #{flag} 1234 bar])
+
+        expect(options.merge_request).to eq 1234
+      end
+    end
+
+    it 'parses --dry-run and -n' do
+      %w[--dry-run -n].each do |flag|
+        options = described_class.parse(%W[foo #{flag} bar])
+
+        expect(options.dry_run).to eq true
+      end
+    end
+
+    it 'parses --git-username and -u' do
+      allow(described_class).to receive(:git_user_name).and_return('Jane Doe')
+
+      %w[--git-username -u].each do |flag|
+        options = described_class.parse(%W[foo #{flag} bar])
+
+        expect(options.author).to eq 'Jane Doe'
+      end
+    end
+
+    it 'parses -h' do
+      expect do
+        $stdout = StringIO.new
+
+        described_class.parse(%w[foo -h bar])
+      end.to raise_error(SystemExit)
+    end
+
+    it 'assigns title' do
+      options = described_class.parse(%W[foo -m 1 bar\n -u baz\r\n --amend])
+
+      expect(options.title).to eq 'foo bar baz'
+    end
+  end
+end
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index b4f066d86009653d941995dd6b0a73dab14361bb..2a7523c6512ec364bd8919d8d5e586f3c496a213 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -14,49 +14,49 @@ describe Projects::ProjectMembersController do
   end
 
   describe 'POST create' do
-    context 'when users are added' do
-      let(:project_user) { create(:user) }
+    let(:project_user) { create(:user) }
 
-      before { sign_in(user) }
+    before { sign_in(user) }
 
-      context 'when user does not have enough rights' do
-        before { project.team << [user, :developer] }
+    context 'when user does not have enough rights' do
+      before { project.team << [user, :developer] }
 
-        it 'returns 404' do
-          post :create, namespace_id: project.namespace,
-                        project_id: project,
-                        user_ids: project_user.id,
-                        access_level: Gitlab::Access::GUEST
+      it 'returns 404' do
+        post :create, namespace_id: project.namespace,
+                      project_id: project,
+                      user_ids: project_user.id,
+                      access_level: Gitlab::Access::GUEST
 
-          expect(response).to have_http_status(404)
-          expect(project.users).not_to include project_user
-        end
+        expect(response).to have_http_status(404)
+        expect(project.users).not_to include project_user
       end
+    end
 
-      context 'when user has enough rights' do
-        before { project.team << [user, :master] }
+    context 'when user has enough rights' do
+      before { project.team << [user, :master] }
 
-        it 'adds user to members' do
-          post :create, namespace_id: project.namespace,
-                        project_id: project,
-                        user_ids: project_user.id,
-                        access_level: Gitlab::Access::GUEST
-
-          expect(response).to set_flash.to 'Users were successfully added.'
-          expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project))
-          expect(project.users).to include project_user
-        end
+      it 'adds user to members' do
+        expect_any_instance_of(Members::CreateService).to receive(:execute).and_return(true)
 
-        it 'adds no user to members' do
-          post :create, namespace_id: project.namespace,
-                        project_id: project,
-                        user_ids: '',
-                        access_level: Gitlab::Access::GUEST
+        post :create, namespace_id: project.namespace,
+                      project_id: project,
+                      user_ids: project_user.id,
+                      access_level: Gitlab::Access::GUEST
 
-          expect(response).to set_flash.to 'No users or groups specified.'
-          expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project))
-          expect(project.users).not_to include project_user
-        end
+        expect(response).to set_flash.to 'Users were successfully added.'
+        expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project))
+      end
+
+      it 'adds no user to members' do
+        expect_any_instance_of(Members::CreateService).to receive(:execute).and_return(false)
+
+        post :create, namespace_id: project.namespace,
+                      project_id: project,
+                      user_ids: '',
+                      access_level: Gitlab::Access::GUEST
+
+        expect(response).to set_flash.to 'No users or groups specified.'
+        expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project))
       end
     end
   end
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 2f82fafc13aaaf0a62fd79afb1f3d10943c3869e..d92c66b689d363eddec190a9078930ca8ab39c4a 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -7,15 +7,16 @@ describe "Admin Runners" do
 
   describe "Runners page" do
     before do
-      runner = FactoryGirl.create(:ci_runner)
+      runner = FactoryGirl.create(:ci_runner, contacted_at: Time.now)
       pipeline = FactoryGirl.create(:ci_pipeline)
       FactoryGirl.create(:ci_build, pipeline: pipeline, runner_id: runner.id)
       visit admin_runners_path
     end
 
-    it { page.has_text? "Manage Runners" }
-    it { page.has_text? "To register a new runner" }
-    it { page.has_text? "Runners with last contact less than a minute ago: 1" }
+    it 'has all necessary texts' do
+      expect(page).to have_text "To register a new Runner"
+      expect(page).to have_text "Runners with last contact less than a minute ago: 1"
+    end
 
     describe 'search' do
       before do
@@ -27,8 +28,10 @@ describe "Admin Runners" do
         search_form.click_button 'Search'
       end
 
-      it { expect(page).to have_content("runner-foo") }
-      it { expect(page).not_to have_content("runner-bar") }
+      it 'shows correct runner' do
+        expect(page).to have_content("runner-foo")
+        expect(page).not_to have_content("runner-bar")
+      end
     end
   end
 
@@ -46,8 +49,10 @@ describe "Admin Runners" do
     end
 
     describe 'projects' do
-      it { expect(page).to have_content(@project1.name_with_namespace) }
-      it { expect(page).to have_content(@project2.name_with_namespace) }
+      it 'contains project names' do
+        expect(page).to have_content(@project1.name_with_namespace)
+        expect(page).to have_content(@project2.name_with_namespace)
+      end
     end
 
     describe 'search' do
@@ -57,8 +62,10 @@ describe "Admin Runners" do
         search_form.click_button 'Search'
       end
 
-      it { expect(page).to have_content(@project1.name_with_namespace) }
-      it { expect(page).not_to have_content(@project2.name_with_namespace) }
+      it 'contains name of correct project' do
+        expect(page).to have_content(@project1.name_with_namespace)
+        expect(page).not_to have_content(@project2.name_with_namespace)
+      end
     end
 
     describe 'enable/create' do
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index 4dd9548cfc51737649d28629e63851f771c4f7d7..21ee6cedbae213fa9b69029d9ec547da9a977218 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -19,6 +19,17 @@ describe "Dashboard Issues Feed", feature: true  do
         expect(body).to have_selector('title', text: "#{user.name} issues")
       end
 
+      it "renders atom feed with url parameters" do
+        visit issues_dashboard_path(:atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
+
+        link = find('link[type="application/atom+xml"]')
+        params = CGI::parse(URI.parse(link[:href]).query)
+
+        expect(params).to include('private_token' => [user.private_token])
+        expect(params).to include('state' => ['opened'])
+        expect(params).to include('assignee_id' => [user.id.to_s])
+      end
+
       context "issue with basic fields" do
         let!(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'test desc') }
 
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index 09c140868fb818bdbc5adb966081040ed09c15fd..863412d18eb7dc36ffec875de38b4638cc47c08a 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -3,10 +3,14 @@ require 'spec_helper'
 describe 'Issues Feed', feature: true  do
   describe 'GET /issues' do
     let!(:user)     { create(:user) }
+    let!(:group)    { create(:group) }
     let!(:project)  { create(:project) }
     let!(:issue)    { create(:issue, author: user, project: project) }
 
-    before { project.team << [user, :developer] }
+    before do
+      project.team << [user, :developer]
+      group.add_developer(user)
+    end
 
     context 'when authenticated' do
       it 'renders atom feed' do
@@ -33,5 +37,28 @@ describe 'Issues Feed', feature: true  do
         expect(body).to have_selector('entry summary', text: issue.title)
       end
     end
+
+    it "renders atom feed with url parameters for project issues" do
+      visit namespace_project_issues_path(project.namespace, project,
+                                          :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
+
+      link = find('link[type="application/atom+xml"]')
+      params = CGI::parse(URI.parse(link[:href]).query)
+
+      expect(params).to include('private_token' => [user.private_token])
+      expect(params).to include('state' => ['opened'])
+      expect(params).to include('assignee_id' => [user.id.to_s])
+    end
+
+    it "renders atom feed with url parameters for group issues" do
+      visit issues_group_path(group, :atom, private_token: user.private_token, state: 'opened', assignee_id: user.id)
+
+      link = find('link[type="application/atom+xml"]')
+      params = CGI::parse(URI.parse(link[:href]).query)
+
+      expect(params).to include('private_token' => [user.private_token])
+      expect(params).to include('state' => ['opened'])
+      expect(params).to include('assignee_id' => [user.id.to_s])
+    end
   end
 end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 3234fabe288979f1b7151dd82fcef7eb395c6b7b..a92075fec8f2e43c546db2e02e737437147b3354 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -53,7 +53,7 @@ describe 'Issue Boards', feature: true, js: true do
   context 'with lists' do
     let(:milestone) { create(:milestone, project: project) }
 
-    let(:planning)    { create(:label, project: project, name: 'Planning') }
+    let(:planning)    { create(:label, project: project, name: 'Planning', description: 'Test') }
     let(:development) { create(:label, project: project, name: 'Development') }
     let(:testing)     { create(:label, project: project, name: 'Testing') }
     let(:bug)         { create(:label, project: project, name: 'Bug') }
@@ -91,6 +91,12 @@ describe 'Issue Boards', feature: true, js: true do
       expect(page).to have_selector('.board', count: 4)
     end
 
+    it 'shows description tooltip on list title' do
+      page.within('.board:nth-child(2)') do
+        expect(find('.board-title span.has-tooltip')[:title]).to eq('Test')
+      end
+    end
+
     it 'shows issues in lists' do
       wait_for_board_cards(2, 2)
       wait_for_board_cards(3, 2)
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 5910803df51f4f2c6541da76827fbe2f4cb8962f..338c53f08a644f29f735230caa0771d8b8d8dc5a 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -64,9 +64,11 @@ describe 'Commits' do
             visit ci_status_path(pipeline)
           end
 
-          it { expect(page).to have_content pipeline.sha[0..7] }
-          it { expect(page).to have_content pipeline.git_commit_message }
-          it { expect(page).to have_content pipeline.git_author_name }
+          it 'shows pipeline`s data' do
+            expect(page).to have_content pipeline.sha[0..7]
+            expect(page).to have_content pipeline.git_commit_message
+            expect(page).to have_content pipeline.git_author_name
+          end
         end
 
         context 'Download artifacts' do
diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard_issues_spec.rb
index 9b54b5301e587d3a3b47ba54222faf39d6510d33..b898f9bc64fb3ca7c065b7df443c1479262751b8 100644
--- a/spec/features/dashboard_issues_spec.rb
+++ b/spec/features/dashboard_issues_spec.rb
@@ -44,6 +44,22 @@ describe "Dashboard Issues filtering", feature: true, js: true do
       expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
       expect(page).to have_selector('.issue', count: 1)
     end
+
+    it 'updates atom feed link' do
+      visit_issues(milestone_title: '', assignee_id: user.id)
+
+      link = find('.nav-controls a', text: 'Subscribe')
+      params = CGI::parse(URI.parse(link[:href]).query)
+      auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
+      auto_discovery_params = CGI::parse(URI.parse(auto_discovery_link[:href]).query)
+
+      expect(params).to include('private_token' => [user.private_token])
+      expect(params).to include('milestone_title' => [''])
+      expect(params).to include('assignee_id' => [user.id.to_s])
+      expect(auto_discovery_params).to include('private_token' => [user.private_token])
+      expect(auto_discovery_params).to include('milestone_title' => [''])
+      expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
+    end
   end
 
   def show_milestone_dropdown
@@ -51,7 +67,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do
     expect(page).to have_selector('.dropdown-content', visible: true)
   end
 
-  def visit_issues
-    visit issues_dashboard_path
+  def visit_issues(*args)
+    visit issues_dashboard_path(*args)
   end
 end
diff --git a/spec/features/issues/filter_by_milestone_spec.rb b/spec/features/issues/filter_by_milestone_spec.rb
index 88e1549a22badae536c5847464341ab938748038..9dfa5d1de1991eb543acc69bbd3023dd4ce14d39 100644
--- a/spec/features/issues/filter_by_milestone_spec.rb
+++ b/spec/features/issues/filter_by_milestone_spec.rb
@@ -11,6 +11,7 @@ feature 'Issue filtering by Milestone', feature: true do
     visit_issues(project)
     filter_by_milestone(Milestone::None.title)
 
+    expect(page).to have_css('.milestone-filter .dropdown-toggle-text', text: 'No Milestone')
     expect(page).to have_css('.issue', count: 1)
   end
 
@@ -22,6 +23,7 @@ feature 'Issue filtering by Milestone', feature: true do
       visit_issues(project)
       filter_by_milestone(Milestone::Upcoming.title)
 
+      expect(page).to have_css('.milestone-filter .dropdown-toggle-text', text: 'Upcoming')
       expect(page).to have_css('.issue', count: 0)
     end
 
@@ -33,6 +35,7 @@ feature 'Issue filtering by Milestone', feature: true do
       visit_issues(project)
       filter_by_milestone(Milestone::Upcoming.title)
 
+      expect(page).to have_css('.milestone-filter .dropdown-toggle-text', text: 'Upcoming')
       expect(page).to have_css('.issue', count: 1)
     end
 
@@ -44,6 +47,7 @@ feature 'Issue filtering by Milestone', feature: true do
       visit_issues(project)
       filter_by_milestone(Milestone::Upcoming.title)
 
+      expect(page).to have_css('.milestone-filter .dropdown-toggle-text', text: 'Upcoming')
       expect(page).to have_css('.issue', count: 0)
     end
   end
@@ -55,6 +59,7 @@ feature 'Issue filtering by Milestone', feature: true do
     visit_issues(project)
     filter_by_milestone(milestone.title)
 
+    expect(page).to have_css('.milestone-filter .dropdown-toggle-text', text: milestone.title)
     expect(page).to have_css('.issue', count: 1)
   end
 
@@ -70,6 +75,7 @@ feature 'Issue filtering by Milestone', feature: true do
       visit_issues(project)
       filter_by_milestone(milestone.title)
 
+      expect(page).to have_css('.milestone-filter .dropdown-toggle-text', text: milestone.title)
       expect(page).to have_css('.issue', count: 1)
     end
   end
diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb
index 78208aed46dd84e59219321d9f2b2c9603fe3512..2798db92f0f7b769045a08854b43284ec3f5f29e 100644
--- a/spec/features/issues/filter_issues_spec.rb
+++ b/spec/features/issues/filter_issues_spec.rb
@@ -4,6 +4,7 @@ describe 'Filter issues', feature: true do
   include WaitForAjax
 
   let!(:project)   { create(:project) }
+  let!(:group)     { create(:group) }
   let!(:user)      { create(:user)}
   let!(:milestone) { create(:milestone, project: project) }
   let!(:label)     { create(:label, project: project) }
@@ -11,6 +12,7 @@ describe 'Filter issues', feature: true do
 
   before do
     project.team << [user, :master]
+    group.add_developer(user)
     login_as(user)
     create(:issue, project: project)
   end
@@ -347,4 +349,36 @@ describe 'Filter issues', feature: true do
       end
     end
   end
+
+  it 'updates atom feed link for project issues' do
+    visit namespace_project_issues_path(project.namespace, project, milestone_title: '', assignee_id: user.id)
+
+    link = find('.nav-controls a', text: 'Subscribe')
+    params = CGI::parse(URI.parse(link[:href]).query)
+    auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
+    auto_discovery_params = CGI::parse(URI.parse(auto_discovery_link[:href]).query)
+
+    expect(params).to include('private_token' => [user.private_token])
+    expect(params).to include('milestone_title' => [''])
+    expect(params).to include('assignee_id' => [user.id.to_s])
+    expect(auto_discovery_params).to include('private_token' => [user.private_token])
+    expect(auto_discovery_params).to include('milestone_title' => [''])
+    expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
+  end
+
+  it 'updates atom feed link for group issues' do
+    visit issues_group_path(group, milestone_title: '', assignee_id: user.id)
+
+    link = find('.nav-controls a', text: 'Subscribe')
+    params = CGI::parse(URI.parse(link[:href]).query)
+    auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
+    auto_discovery_params = CGI::parse(URI.parse(auto_discovery_link[:href]).query)
+
+    expect(params).to include('private_token' => [user.private_token])
+    expect(params).to include('milestone_title' => [''])
+    expect(params).to include('assignee_id' => [user.id.to_s])
+    expect(auto_discovery_params).to include('private_token' => [user.private_token])
+    expect(auto_discovery_params).to include('milestone_title' => [''])
+    expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
+  end
 end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index b963d1305b5315314ff50e6a1f2f869f776bbc6e..c68e1ea4af964a4ef725649c4ba8b1df4a49a76f 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -59,4 +59,12 @@ feature 'Create New Merge Request', feature: true, js: true do
       expect(page).to have_css('a.btn.active', text: 'Side-by-side')
     end
   end
+
+  it 'does not allow non-existing branches' do
+    visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_branch: 'non-exist-target', source_branch: 'non-exist-source' })
+
+    expect(page).to have_content('The form contains the following errors')
+    expect(page).to have_content('Source branch "non-exist-source" does not exist')
+    expect(page).to have_content('Target branch "non-exist-target" does not exist')
+  end
 end
diff --git a/spec/features/merge_requests/edit_mr_spec.rb b/spec/features/merge_requests/edit_mr_spec.rb
index c77e719c5df66353b6b534585fc734f2d375dd73..c46bd8d449f376589c9be1acd117ac163a3372bc 100644
--- a/spec/features/merge_requests/edit_mr_spec.rb
+++ b/spec/features/merge_requests/edit_mr_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 feature 'Edit Merge Request', feature: true do
   let(:user) { create(:user) }
   let(:project) { create(:project, :public) }
-  let(:merge_request) { create(:merge_request, :with_diffs, source_project: project) }
+  let(:merge_request) { create(:merge_request, :simple, source_project: project) }
 
   before do
     project.team << [user, :master]
@@ -28,5 +28,17 @@ feature 'Edit Merge Request', feature: true do
 
       expect(page).to have_content 'Someone edited the merge request the same time you did'
     end
+
+    it 'allows to unselect "Remove source branch"' do
+      merge_request.update(merge_params: { 'force_remove_source_branch' => '1' })
+      expect(merge_request.merge_params['force_remove_source_branch']).to be_truthy
+
+      visit edit_namespace_project_merge_request_path(project.namespace, project, merge_request)
+      uncheck 'Remove source branch when merge request is accepted'
+
+      click_button 'Save changes'
+
+      expect(page).to have_content 'Remove source branch'
+    end
   end
 end
diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb
index d1685f95503c94eba9a1a1e4ad4af8c528aee109..a8022a5361f9e25b971618fbc177c2c90e69a0c1 100644
--- a/spec/features/projects/builds_spec.rb
+++ b/spec/features/projects/builds_spec.rb
@@ -79,12 +79,14 @@ describe "Builds" do
       click_link "Cancel running"
     end
 
-    it { expect(page).to have_selector('.nav-links li.active', text: 'All') }
-    it { expect(page).to have_content 'canceled' }
-    it { expect(page).to have_content @build.short_sha }
-    it { expect(page).to have_content @build.ref }
-    it { expect(page).to have_content @build.name }
-    it { expect(page).not_to have_link 'Cancel running' }
+    it 'shows all necessary content' do
+      expect(page).to have_selector('.nav-links li.active', text: 'All')
+      expect(page).to have_content 'canceled'
+      expect(page).to have_content @build.short_sha
+      expect(page).to have_content @build.ref
+      expect(page).to have_content @build.name
+      expect(page).not_to have_link 'Cancel running'
+    end
   end
 
   describe "GET /:project/builds/:id" do
@@ -93,10 +95,12 @@ describe "Builds" do
         visit namespace_project_build_path(@project.namespace, @project, @build)
       end
 
-      it { expect(page.status_code).to eq(200) }
-      it { expect(page).to have_content @commit.sha[0..7] }
-      it { expect(page).to have_content @commit.git_commit_message }
-      it { expect(page).to have_content @commit.git_author_name }
+      it 'shows commit`s data' do
+        expect(page.status_code).to eq(200)
+        expect(page).to have_content @commit.sha[0..7]
+        expect(page).to have_content @commit.git_commit_message
+        expect(page).to have_content @commit.git_author_name
+      end
     end
 
     context "Build from other project" do
@@ -167,7 +171,7 @@ describe "Builds" do
 
     describe 'Variables' do
       before do
-        @trigger_request = create :ci_trigger_request_with_variables 
+        @trigger_request = create :ci_trigger_request_with_variables
         @build = create :ci_build, pipeline: @commit, trigger_request: @trigger_request
         visit namespace_project_build_path(@project.namespace, @project, @build)
       end
@@ -176,14 +180,14 @@ describe "Builds" do
         expect(page).to have_css('.reveal-variables')
         expect(page).not_to have_css('.js-build-variable')
         expect(page).not_to have_css('.js-build-value')
-     
+
         click_button 'Reveal Variables'
 
         expect(page).not_to have_css('.reveal-variables')
         expect(page).to have_selector('.js-build-variable', text: 'TRIGGER_KEY_1')
         expect(page).to have_selector('.js-build-value', text: 'TRIGGER_VALUE_1')
       end
-    end    
+    end
   end
 
   describe "POST /:project/builds/:id/cancel" do
@@ -194,9 +198,11 @@ describe "Builds" do
         click_link "Cancel"
       end
 
-      it { expect(page.status_code).to eq(200) }
-      it { expect(page).to have_content 'canceled' }
-      it { expect(page).to have_content 'Retry' }
+      it 'loads the page and shows all needed controls' do
+        expect(page.status_code).to eq(200)
+        expect(page).to have_content 'canceled'
+        expect(page).to have_content 'Retry'
+      end
     end
 
     context "Build from other project" do
@@ -216,7 +222,9 @@ describe "Builds" do
         @build.run!
         visit namespace_project_build_path(@project.namespace, @project, @build)
         click_link 'Cancel'
-        click_link 'Retry'
+        page.within('.build-header') do
+          click_link 'Retry build'
+        end
       end
 
       it 'shows the right status and buttons' do
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index d25cf7fb35352a9e7b0a1974654e61668f489cbb..09aa6758b5cae5603cc69d39707bd3c4893209fe 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -164,5 +164,38 @@ describe 'Edit Project Settings', feature: true do
 
       expect(page).to have_content "Customize your workflow!"
     end
+
+    it "hides project activity tabs" do
+      select "Disabled", from: "project_project_feature_attributes_repository_access_level"
+      select "Disabled", from: "project_project_feature_attributes_issues_access_level"
+      select "Disabled", from: "project_project_feature_attributes_wiki_access_level"
+
+      click_button "Save changes"
+      wait_for_ajax
+
+      visit activity_namespace_project_path(project.namespace, project)
+
+      page.within(".event-filter") do
+        expect(page).to have_selector("a", count: 2)
+        expect(page).not_to have_content("Push events")
+        expect(page).not_to have_content("Merge events")
+        expect(page).not_to have_content("Comments")
+      end
+    end
+  end
+
+  # Regression spec for https://gitlab.com/gitlab-org/gitlab-ce/issues/24056
+  describe 'project statistic visibility' do
+    let!(:project) { create(:project, :private) }
+
+    before do
+      project.team << [member, :guest]
+      login_as(member)
+      visit namespace_project_path(project.namespace, project)
+    end
+
+    it "does not show project statistic for guest" do
+      expect(page).not_to have_selector('.project-stats')
+    end
   end
 end
diff --git a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b4f5f6b3fc5f10ede29a8e8f354dc8a157cf04e2
--- /dev/null
+++ b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe 'Projects > Wiki > User views wiki in project page', feature: true do
+  let(:user) { create(:user) }
+  let(:project) { create(:empty_project) }
+
+  before do
+    project.team << [user, :master]
+    login_as(user)
+  end
+
+  context 'when repository is disabled for project' do
+    before do
+      project.project_feature.update!(
+        repository_access_level: ProjectFeature::DISABLED,
+        merge_requests_access_level: ProjectFeature::DISABLED,
+        builds_access_level: ProjectFeature::DISABLED
+      )
+    end
+
+    context 'when wiki homepage contains a link' do
+      before do
+        WikiPages::CreateService.new(
+          project,
+          user,
+          title: 'home',
+          content: '[some link](other-page)'
+        ).execute
+      end
+
+      it 'displays the correct URL for the link' do
+        visit namespace_project_path(project.namespace, project)
+        expect(page).to have_link(
+          'some link',
+          href: namespace_project_wiki_path(
+            project.namespace,
+            project,
+            'other-page'
+          )
+        )
+      end
+    end
+  end
+end
diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb
index 230543cd17548f9bff5725a5dad8fe1f1434d8d9..3ae83ac082d5ea3aff2496704cb2b1176e5607bf 100644
--- a/spec/features/todos/todos_spec.rb
+++ b/spec/features/todos/todos_spec.rb
@@ -13,7 +13,7 @@ describe 'Dashboard Todos', feature: true do
         visit dashboard_todos_path
       end
       it 'shows "All done" message' do
-        expect(page).to have_content "You're all done!"
+        expect(page).to have_content "Todos let you see what you should do next."
       end
     end
 
@@ -44,7 +44,7 @@ describe 'Dashboard Todos', feature: true do
         end
 
         it 'shows "All done" message' do
-          expect(page).to have_content("You're all done!")
+          expect(page).to have_content("Good job! Looks like you don't have any todos left.")
         end
       end
 
@@ -64,7 +64,7 @@ describe 'Dashboard Todos', feature: true do
           end
 
           it 'shows "All done" message' do
-            expect(page).to have_content("You're all done!")
+            expect(page).to have_content("Good job! Looks like you don't have any todos left.")
           end
         end
       end
@@ -152,7 +152,7 @@ describe 'Dashboard Todos', feature: true do
         within('.todos-pending-count') { expect(page).to have_content '0' }
         expect(page).to have_content 'To do 0'
         expect(page).to have_content 'Done 0'
-        expect(page).to have_content "You're all done!"
+        expect(page).to have_content "Good job! Looks like you don't have any todos left."
       end
     end
   end
diff --git a/spec/javascripts/fixtures/gl_field_errors.html.haml b/spec/javascripts/fixtures/gl_field_errors.html.haml
index 2526e5e33a5671ce10839dee46f9e68fd58cd6e3..69445b61367c201cf050864a6c1e17248e32d108 100644
--- a/spec/javascripts/fixtures/gl_field_errors.html.haml
+++ b/spec/javascripts/fixtures/gl_field_errors.html.haml
@@ -1,4 +1,4 @@
-%form.show-gl-field-errors{action: 'submit', method: 'post'}
+%form.gl-show-field-errors{action: 'submit', method: 'post'}
   .form-group
     %input.required-text{required: true, type: 'text'} Text
   .form-group
@@ -10,6 +10,6 @@
   .form-group
     %input.hidden{ type:'hidden' }
   .form-group
-    %input.custom.no-gl-field-errors{ type:'text' } Custom, do not validate
+    %input.custom.gl-field-error-ignore{ 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
index 4bdd72800eaeccd774e274937ba3afd50f1e90ca..0713e30e485692b48e9ceb5fe9f098c47daf85ce 100644
--- a/spec/javascripts/gl_field_errors_spec.js.es6
+++ b/spec/javascripts/gl_field_errors_spec.js.es6
@@ -8,7 +8,7 @@
   describe('GL Style Field Errors', function() {
     beforeEach(function() {
       fixture.load('gl_field_errors.html');
-      const $form = this.$form = $('form.show-gl-field-errors');
+      const $form = this.$form = $('form.gl-show-field-errors');
       this.fieldErrors = new global.GlFieldErrors($form);
     });
 
@@ -21,7 +21,7 @@
     });
 
     it('should ignore elements with custom error handling', function() {
-      const customErrorFlag = 'no-gl-field-errors';
+      const customErrorFlag = 'gl-field-error-ignore';
       const customErrorElem = $(`.${customErrorFlag}`);
 
       expect(customErrorElem.length).toBe(1);
diff --git a/spec/javascripts/merge_request_widget_spec.js b/spec/javascripts/merge_request_widget_spec.js
index 1e2072f370aacd413b7974f6e63fb36dc02f6cb3..49dfeab61d834237891808d32c38bb9f85e654b2 100644
--- a/spec/javascripts/merge_request_widget_spec.js
+++ b/spec/javascripts/merge_request_widget_spec.js
@@ -1,6 +1,6 @@
 /* eslint-disable */
 /*= require merge_request_widget */
-/*= require lib/utils/jquery.timeago.js */
+/*= require jquery.timeago.js */
 
 (function() {
   describe('MergeRequestWidget', function() {
diff --git a/spec/javascripts/spec_helper.js b/spec/javascripts/spec_helper.js
index bdce2465fbf4fe329fde64c2eace15673df717e5..9cb8243ee2cb3f6fc74bd1a5e75085b90cf88d5b 100644
--- a/spec/javascripts/spec_helper.js
+++ b/spec/javascripts/spec_helper.js
@@ -28,7 +28,7 @@
 // setTimeout(Teaspoon.execute, 1000)
 // Matching files
 // By default Teaspoon will look for files that match
-// _spec.{js,js.coffee,.coffee}. Add a filename_spec.js file in your spec path
+// _spec.{js,js.es6}. Add a filename_spec.js file in your spec path
 // and it'll be included in the default suite automatically. If you want to
 // customize suites, check out the configuration in teaspoon_env.rb
 // Manifest
diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index 2f9343fadaff067ba40c74f1e6fd670863f6c988..fbf7a461fa5d55f8955118666b0d9e75483514ac 100644
--- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -8,6 +8,8 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
   end
 
   shared_examples_for "external issue tracker" do
+    it_behaves_like 'a reference containing an element node'
+
     it 'requires project context' do
       expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
     end
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index a2025672ad9d6d9b276a2a873bce8e875f1be4d1..8f0b2db3e8e820755b88343884e8e319d651508b 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -22,6 +22,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
   end
 
   context 'internal reference' do
+    it_behaves_like 'a reference containing an element node'
+
     let(:reference) { issue.to_reference }
 
     it 'ignores valid references when using non-default tracker' do
@@ -83,6 +85,20 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
       expect(link.attr('data-issue')).to eq issue.id.to_s
     end
 
+    it 'includes a data-original attribute' do
+      doc = reference_filter("See #{reference}")
+      link = doc.css('a').first
+
+      expect(link).to have_attribute('data-original')
+      expect(link.attr('data-original')).to eq reference
+    end
+
+    it 'does not escape the data-original attribute' do
+      inner_html = 'element <code>node</code> inside'
+      doc = reference_filter(%{<a href="#{reference}">#{inner_html}</a>})
+      expect(doc.children.first.attr('data-original')).to eq inner_html
+    end
+
     it 'supports an :only_path context' do
       doc = reference_filter("Issue #{reference}", only_path: true)
       link = doc.css('a').first.attr('href')
@@ -101,6 +117,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
   end
 
   context 'cross-project reference' do
+    it_behaves_like 'a reference containing an element node'
+
     let(:namespace) { create(:namespace, name: 'cross-reference') }
     let(:project2)  { create(:empty_project, :public, namespace: namespace) }
     let(:issue)     { create(:issue, project: project2) }
@@ -141,6 +159,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
   end
 
   context 'cross-project URL reference' do
+    it_behaves_like 'a reference containing an element node'
+
     let(:namespace) { create(:namespace, name: 'cross-reference') }
     let(:project2)  { create(:empty_project, :public, namespace: namespace) }
     let(:issue)     { create(:issue, project: project2) }
@@ -160,39 +180,45 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
   end
 
   context 'cross-project reference in link href' do
+    it_behaves_like 'a reference containing an element node'
+
     let(:namespace) { create(:namespace, name: 'cross-reference') }
     let(:project2)  { create(:empty_project, :public, namespace: namespace) }
     let(:issue)     { create(:issue, project: project2) }
-    let(:reference) { %Q{<a href="#{issue.to_reference(project)}">Reference</a>} }
+    let(:reference) { issue.to_reference(project) }
+    let(:reference_link) { %{<a href="#{reference}">Reference</a>} }
 
     it 'links to a valid reference' do
-      doc = reference_filter("See #{reference}")
+      doc = reference_filter("See #{reference_link}")
 
       expect(doc.css('a').first.attr('href')).
         to eq helper.url_for_issue(issue.iid, project2)
     end
 
     it 'links with adjacent text' do
-      doc = reference_filter("Fixed (#{reference}.)")
+      doc = reference_filter("Fixed (#{reference_link}.)")
       expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/)
     end
   end
 
   context 'cross-project URL in link href' do
+    it_behaves_like 'a reference containing an element node'
+
     let(:namespace) { create(:namespace, name: 'cross-reference') }
     let(:project2)  { create(:empty_project, :public, namespace: namespace) }
     let(:issue)     { create(:issue, project: project2) }
-    let(:reference) { %Q{<a href="#{helper.url_for_issue(issue.iid, project2) + "#note_123"}">Reference</a>} }
+    let(:reference) { "#{helper.url_for_issue(issue.iid, project2) + "#note_123"}" }
+    let(:reference_link) { %{<a href="#{reference}">Reference</a>} }
 
     it 'links to a valid reference' do
-      doc = reference_filter("See #{reference}")
+      doc = reference_filter("See #{reference_link}")
 
       expect(doc.css('a').first.attr('href')).
         to eq helper.url_for_issue(issue.iid, project2) + "#note_123"
     end
 
     it 'links with adjacent text' do
-      doc = reference_filter("Fixed (#{reference}.)")
+      doc = reference_filter("Fixed (#{reference_link}.)")
       expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/)
     end
   end
diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb
index 729e77fd43fc28c4feadef6f5bea393831d94159..5bfeb82e738c41e87787eccce894d06c8e2844b8 100644
--- a/spec/lib/banzai/filter/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb
@@ -24,6 +24,8 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
   end
 
   context 'mentioning @all' do
+    it_behaves_like 'a reference containing an element node'
+
     let(:reference) { User.reference_prefix + 'all' }
 
     before do
@@ -60,6 +62,8 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
   end
 
   context 'mentioning a user' do
+    it_behaves_like 'a reference containing an element node'
+
     it 'links to a User' do
       doc = reference_filter("Hey #{reference}")
       expect(doc.css('a').first.attr('href')).to eq urls.user_url(user)
@@ -89,6 +93,8 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do
   end
 
   context 'mentioning a group' do
+    it_behaves_like 'a reference containing an element node'
+
     let(:group)     { create(:group) }
     let(:reference) { group.to_reference }
 
diff --git a/spec/lib/banzai/pipeline/full_pipeline_spec.rb b/spec/lib/banzai/pipeline/full_pipeline_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2501b638774789070bb9eee3805c678b4e43ee19
--- /dev/null
+++ b/spec/lib/banzai/pipeline/full_pipeline_spec.rb
@@ -0,0 +1,28 @@
+require 'rails_helper'
+
+describe Banzai::Pipeline::FullPipeline do
+  describe 'References' do
+    let(:project) { create(:empty_project, :public) }
+    let(:issue)   { create(:issue, project: project) }
+
+    it 'handles markdown inside a reference' do
+      markdown = "[some `code` inside](#{issue.to_reference})"
+      result = described_class.call(markdown, project: project)
+      link_content = result[:output].css('a').inner_html
+      expect(link_content).to eq('some <code>code</code> inside')
+    end
+
+    it 'sanitizes reference HTML' do
+      link_label = '<script>bad things</script>'
+      markdown = "[#{link_label}](#{issue.to_reference})"
+      result = described_class.to_html(markdown, project: project)
+      expect(result).not_to include(link_label)
+    end
+
+    it 'escapes the data-original attribute on a reference' do
+      markdown = %Q{[">bad things](#{issue.to_reference})}
+      result = described_class.to_html(markdown, project: project)
+      expect(result).to include(%{data-original='\"&gt;bad things'})
+    end
+  end
+end
diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb
index 254657a881da3a40a038dbca0adfe06e85e44c9b..6d2c141e18b2bde4978b731bc88aaa598042a408 100644
--- a/spec/lib/banzai/redactor_spec.rb
+++ b/spec/lib/banzai/redactor_spec.rb
@@ -6,39 +6,60 @@ describe Banzai::Redactor do
   let(:redactor) { described_class.new(project, user) }
 
   describe '#redact' do
-    it 'redacts an Array of documents' do
-      doc1 = Nokogiri::HTML.
-        fragment('<a class="gfm" data-reference-type="issue">foo</a>')
-
-      doc2 = Nokogiri::HTML.
-        fragment('<a class="gfm" data-reference-type="issue">bar</a>')
-
-      expect(redactor).to receive(:nodes_visible_to_user).and_return([])
-
-      redacted_data = redactor.redact([doc1, doc2])
-
-      expect(redacted_data.map { |data| data[:document] }).to eq([doc1, doc2])
-      expect(redacted_data.map { |data| data[:visible_reference_count] }).to eq([0, 0])
-      expect(doc1.to_html).to eq('foo')
-      expect(doc2.to_html).to eq('bar')
+    context 'when reference not visible to user' do
+      before do
+        expect(redactor).to receive(:nodes_visible_to_user).and_return([])
+      end
+
+      it 'redacts an array of documents' do
+        doc1 = Nokogiri::HTML.
+               fragment('<a class="gfm" data-reference-type="issue">foo</a>')
+
+        doc2 = Nokogiri::HTML.
+               fragment('<a class="gfm" data-reference-type="issue">bar</a>')
+
+        redacted_data = redactor.redact([doc1, doc2])
+
+        expect(redacted_data.map { |data| data[:document] }).to eq([doc1, doc2])
+        expect(redacted_data.map { |data| data[:visible_reference_count] }).to eq([0, 0])
+        expect(doc1.to_html).to eq('foo')
+        expect(doc2.to_html).to eq('bar')
+      end
+
+      it 'replaces redacted reference with inner HTML' do
+        doc = Nokogiri::HTML.fragment("<a class='gfm' data-reference-type='issue'>foo</a>")
+        redactor.redact([doc])
+        expect(doc.to_html).to eq('foo')
+      end
+
+      context 'when data-original attribute provided' do
+        let(:original_content) { '<code>foo</code>' }
+        it 'replaces redacted reference with original content' do
+          doc = Nokogiri::HTML.fragment("<a class='gfm' data-reference-type='issue' data-original='#{original_content}'>bar</a>")
+          redactor.redact([doc])
+          expect(doc.to_html).to eq(original_content)
+        end
+      end
     end
 
-    it 'does not redact an Array of documents' do
-      doc1_html = '<a class="gfm" data-reference-type="issue">foo</a>'
-      doc1 = Nokogiri::HTML.fragment(doc1_html)
+    context 'when reference visible to user' do
+      it 'does not redact an array of documents' do
+        doc1_html = '<a class="gfm" data-reference-type="issue">foo</a>'
+        doc1 = Nokogiri::HTML.fragment(doc1_html)
 
-      doc2_html = '<a class="gfm" data-reference-type="issue">bar</a>'
-      doc2 = Nokogiri::HTML.fragment(doc2_html)
+        doc2_html = '<a class="gfm" data-reference-type="issue">bar</a>'
+        doc2 = Nokogiri::HTML.fragment(doc2_html)
 
-      nodes = redactor.document_nodes([doc1, doc2]).map { |x| x[:nodes] }
-      expect(redactor).to receive(:nodes_visible_to_user).and_return(nodes.flatten)
+        nodes = redactor.document_nodes([doc1, doc2]).map { |x| x[:nodes] }
+        expect(redactor).to receive(:nodes_visible_to_user).and_return(nodes.flatten)
 
-      redacted_data = redactor.redact([doc1, doc2])
+        redacted_data = redactor.redact([doc1, doc2])
 
-      expect(redacted_data.map { |data| data[:document] }).to eq([doc1, doc2])
-      expect(redacted_data.map { |data| data[:visible_reference_count] }).to eq([1, 1])
-      expect(doc1.to_html).to eq(doc1_html)
-      expect(doc2.to_html).to eq(doc2_html)
+        expect(redacted_data.map { |data| data[:document] }).to eq([doc1, doc2])
+        expect(redacted_data.map { |data| data[:visible_reference_count] }).to eq([1, 1])
+        expect(doc1.to_html).to eq(doc1_html)
+        expect(doc2.to_html).to eq(doc2_html)
+      end
     end
   end
 
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index a5aa387f4f7b6b38c1190bf4daaa719230466fba..62aa212f1f6d0f650fc0d9817c957a4b831cebdf 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -122,6 +122,14 @@ describe Gitlab::GitAccess, lib: true do
     describe 'build authentication_abilities permissions' do
       let(:authentication_abilities) { build_authentication_abilities }
 
+      describe 'owner' do
+        let(:project) { create(:project, namespace: user.namespace) }
+
+        context 'pull code' do
+          it { expect(subject).to be_allowed }
+        end
+      end
+
       describe 'reporter user' do
         before { project.team << [user, :reporter] }
 
diff --git a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
index b8e7932eb4aca0ed7cf501d17ebc1d973850c4e7..63bab0f0d0d261fefc8a56d9072b1814c325fdeb 100644
--- a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
+++ b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
@@ -1,8 +1,10 @@
 require 'spec_helper'
 
 describe Gitlab::ImportExport::AttributeCleaner, lib: true do
+  let(:relation_class){ double('relation_class').as_null_object }
   let(:unsafe_hash) do
     {
+      'id' => 101,
       'service_id' => 99,
       'moved_to_id' => 99,
       'namespace_id' => 99,
@@ -27,8 +29,9 @@ describe Gitlab::ImportExport::AttributeCleaner, lib: true do
   end
 
   it 'removes unwanted attributes from the hash' do
-    described_class.clean!(relation_hash: unsafe_hash)
+    # allow(relation_class).to receive(:attribute_method?).and_return(true)
+    parsed_hash = described_class.clean(relation_hash: unsafe_hash, relation_class: relation_class)
 
-    expect(unsafe_hash).to eq(post_safe_hash)
+    expect(parsed_hash).to eq(post_safe_hash)
   end
 end
diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a88ddd17aca69356ca3745466c8806d5a45825b9
--- /dev/null
+++ b/spec/lib/gitlab/import_export/file_importer_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::FileImporter, lib: true do
+  let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') }
+  let(:export_path) { "#{Dir::tmpdir}/file_importer_spec" }
+  let(:valid_file) { "#{shared.export_path}/valid.json" }
+  let(:symlink_file) { "#{shared.export_path}/invalid.json" }
+  let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" }
+
+  before do
+    stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
+    allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+    allow_any_instance_of(Gitlab::ImportExport::CommandLineUtil).to receive(:untar_zxf).and_return(true)
+
+    setup_files
+
+    described_class.import(archive_file: '', shared: shared)
+  end
+
+  after do
+    FileUtils.rm_rf(export_path)
+  end
+
+  it 'removes symlinks in root folder' do
+    expect(File.exist?(symlink_file)).to be false
+  end
+
+  it 'removes symlinks in subfolders' do
+    expect(File.exist?(subfolder_symlink_file)).to be false
+  end
+
+  it 'does not remove a valid file' do
+    expect(File.exist?(valid_file)).to be true
+  end
+
+  def setup_files
+    FileUtils.mkdir_p("#{shared.export_path}/subfolder/")
+    FileUtils.touch(valid_file)
+    FileUtils.ln_s(valid_file, symlink_file)
+    FileUtils.ln_s(valid_file, subfolder_symlink_file)
+  end
+end
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 069ea960321c485c7af3c1a8ca7aa65295cfe075..3038ab53ad8bf945769ab7559ecafe213dd0de54 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -1,4 +1,5 @@
 require 'spec_helper'
+include ImportExport::CommonUtil
 
 describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
   describe 'restore project tree' do
@@ -175,6 +176,19 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
           expect(MergeRequest.find_by_title('MR2').source_project_id).to eq(-1)
         end
       end
+
+      context 'project.json file access check' do
+        it 'does not read a symlink' do
+          Dir.mktmpdir do |tmpdir|
+            setup_symlink(tmpdir, 'project.json')
+            allow(shared).to receive(:export_path).and_call_original
+
+            restored_project_json
+
+            expect(shared.errors.first).not_to include('test')
+          end
+        end
+      end
     end
   end
 end
diff --git a/spec/lib/gitlab/import_export/version_checker_spec.rb b/spec/lib/gitlab/import_export/version_checker_spec.rb
index c680e712b591718e3a27b7a4c68abe06b55f6535..2405ac5abfe3ea5613f6012a191436b96c2d47f3 100644
--- a/spec/lib/gitlab/import_export/version_checker_spec.rb
+++ b/spec/lib/gitlab/import_export/version_checker_spec.rb
@@ -1,8 +1,10 @@
 require 'spec_helper'
+include ImportExport::CommonUtil
 
 describe Gitlab::ImportExport::VersionChecker, services: true do
+  let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: '') }
+
   describe 'bundle a project Git repo' do
-    let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: '') }
     let(:version) { Gitlab::ImportExport.version }
 
     before do
@@ -27,4 +29,16 @@ describe Gitlab::ImportExport::VersionChecker, services: true do
       end
     end
   end
+
+  describe 'version file access check' do
+    it 'does not read a symlink' do
+      Dir.mktmpdir do |tmpdir|
+        setup_symlink(tmpdir, 'VERSION')
+
+        described_class.check!(shared: shared)
+
+        expect(shared.errors.first).not_to include('test')
+      end
+    end
+  end
 end
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d5d87310874c0d145ae09a3e81762d39c3658526
--- /dev/null
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -0,0 +1,35 @@
+describe Gitlab::Utils, lib: true do
+  def to_boolean(value)
+    described_class.to_boolean(value)
+  end
+
+  describe '.to_boolean' do
+    it 'accepts booleans' do
+      expect(to_boolean(true)).to be(true)
+      expect(to_boolean(false)).to be(false)
+    end
+
+    it 'converts a valid string to a boolean' do
+      expect(to_boolean(true)).to be(true)
+      expect(to_boolean('true')).to be(true)
+      expect(to_boolean('YeS')).to be(true)
+      expect(to_boolean('t')).to be(true)
+      expect(to_boolean('1')).to be(true)
+      expect(to_boolean('ON')).to be(true)
+
+      expect(to_boolean('FaLse')).to be(false)
+      expect(to_boolean('F')).to be(false)
+      expect(to_boolean('NO')).to be(false)
+      expect(to_boolean('n')).to be(false)
+      expect(to_boolean('0')).to be(false)
+      expect(to_boolean('oFF')).to be(false)
+    end
+
+    it 'converts an invalid string to nil' do
+      expect(to_boolean('fals')).to be_nil
+      expect(to_boolean('yeah')).to be_nil
+      expect(to_boolean('')).to be_nil
+      expect(to_boolean(nil)).to be_nil
+    end
+  end
+end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index cc215d252f9cec3e3118a32eb6a7b4e58c1108dd..2b76e056f3c01a3b931f31f5a4e4f10052f099ac 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -41,14 +41,62 @@ describe ApplicationSetting, models: true do
       subject { setting }
     end
 
-    context 'repository storages inclussion' do
+    # Upgraded databases will have this sort of content
+    context 'repository_storages is a String, not an Array' do
+      before { setting.__send__(:raw_write_attribute, :repository_storages, 'default') }
+
+      it { expect(setting.repository_storages_before_type_cast).to eq('default') }
+      it { expect(setting.repository_storages).to eq(['default']) }
+    end
+
+    context 'repository storages' do
       before do
-        storages = { 'custom' => 'tmp/tests/custom_repositories' }
+        storages = {
+          'custom1' => 'tmp/tests/custom_repositories_1',
+          'custom2' => 'tmp/tests/custom_repositories_2',
+          'custom3' => 'tmp/tests/custom_repositories_3',
+
+        }
         allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
       end
 
-      it { is_expected.to allow_value('custom').for(:repository_storage) }
-      it { is_expected.not_to allow_value('alternative').for(:repository_storage) }
+      describe 'inclusion' do
+        it { is_expected.to allow_value('custom1').for(:repository_storages) }
+        it { is_expected.to allow_value(['custom2', 'custom3']).for(:repository_storages) }
+        it { is_expected.not_to allow_value('alternative').for(:repository_storages) }
+        it { is_expected.not_to allow_value(['alternative', 'custom1']).for(:repository_storages) }
+      end
+
+      describe 'presence' do
+        it { is_expected.not_to allow_value([]).for(:repository_storages) }
+        it { is_expected.not_to allow_value("").for(:repository_storages) }
+        it { is_expected.not_to allow_value(nil).for(:repository_storages) }
+      end
+
+      describe '.pick_repository_storage' do
+        it 'uses Array#sample to pick a random storage' do
+          array = double('array', sample: 'random')
+          expect(setting).to receive(:repository_storages).and_return(array)
+
+          expect(setting.pick_repository_storage).to eq('random')
+        end
+
+        describe '#repository_storage' do
+          it 'returns the first storage' do
+            setting.repository_storages = ['good', 'bad']
+
+            expect(setting.repository_storage).to eq('good')
+          end
+        end
+
+        describe '#repository_storage=' do
+          it 'overwrites repository_storages' do
+            setting.repository_storage = 'overwritten'
+
+            expect(setting.repository_storages).to eq(['overwritten'])
+          end
+        end
+      end
     end
   end
 
diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb
index 5363aea4d22cc5d642908523fc60d0918514048f..9041690023f0a7719ee0de805ed7e17652f256a4 100644
--- a/spec/models/concerns/project_features_compatibility_spec.rb
+++ b/spec/models/concerns/project_features_compatibility_spec.rb
@@ -22,4 +22,18 @@ describe ProjectFeaturesCompatibility do
       expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED)
     end
   end
+
+  it "converts fields from true to ProjectFeature::ENABLED" do
+    features.each do |feature|
+      project.update_attribute("#{feature}_enabled".to_sym, true)
+      expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED)
+    end
+  end
+
+  it "converts fields from false to ProjectFeature::DISABLED" do
+    features.each do |feature|
+      project.update_attribute("#{feature}_enabled".to_sym, false)
+      expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED)
+    end
+  end
 end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index a9f637147d1e771915275a24d725a8f4842db07d..ee0e38bd3736351e0ab43e57e9b29397c36cccfa 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -1,6 +1,8 @@
 require 'spec_helper'
 
 describe JiraService, models: true do
+  include Gitlab::Routing.url_helpers
+
   describe "Associations" do
     it { is_expected.to belong_to :project }
     it { is_expected.to have_one :service_hook }
@@ -66,6 +68,29 @@ describe JiraService, models: true do
       ).once
     end
 
+    it "references the GitLab commit/merge request" do
+      @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+
+      expect(WebMock).to have_requested(:post, @comment_url).with(
+        body: /#{Gitlab.config.gitlab.url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/
+      ).once
+    end
+
+    it "references the GitLab commit/merge request (relative URL)" do
+      stub_config_setting(relative_url_root: '/gitlab')
+      stub_config_setting(url: Settings.send(:build_gitlab_url))
+
+      allow(JiraService).to receive(:default_url_options) do
+        { script_name: '/gitlab' }
+      end
+
+      @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
+
+      expect(WebMock).to have_requested(:post, @comment_url).with(
+        body: /#{Gitlab.config.gitlab.url}\/#{project.path_with_namespace}\/commit\/#{merge_request.diff_head_sha}/
+      ).once
+    end
+
     it "calls the api with jira_issue_transition_id" do
       @jira_service.jira_issue_transition_id = 'this-is-a-custom-id'
       @jira_service.execute(merge_request, ExternalIssue.new("JIRA-123", project))
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index f4dda1ee5589e4ef2436bb661d26607b9540be75..0245897938c14f85fa0ba945528d23fe0047084e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -67,11 +67,11 @@ describe Project, models: true do
     it { is_expected.to have_many(:notification_settings).dependent(:destroy) }
     it { is_expected.to have_many(:forks).through(:forked_project_links) }
 
-    context 'after create' do
-      it "creates project feature" do
+    context 'after initialized' do
+      it "has a project_feature" do
         project = FactoryGirl.build(:project)
 
-        expect { project.save }.to change{ project.project_feature.present? }.from(false).to(true)
+        expect(project.project_feature.present?).to be_present
       end
     end
 
@@ -837,16 +837,19 @@ describe Project, models: true do
   context 'repository storage by default' do
     let(:project) { create(:empty_project) }
 
-    subject { project.repository_storage }
-
     before do
-      storages = { 'alternative_storage' => '/some/path' }
+      storages = {
+        'default' => 'tmp/tests/repositories',
+        'picked'  => 'tmp/tests/repositories',
+      }
       allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
-      stub_application_setting(repository_storage: 'alternative_storage')
-      allow_any_instance_of(Project).to receive(:ensure_dir_exist).and_return(true)
     end
 
-    it { is_expected.to eq('alternative_storage') }
+    it 'picks storage from ApplicationSetting' do
+      expect_any_instance_of(ApplicationSetting).to receive(:pick_repository_storage).and_return('picked')
+
+      expect(project.repository_storage).to eq('picked')
+    end
   end
 
   context 'shared runners by default' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 10c39b902128bd66d970d1945b037dc681ff4459..d1ed774a914596ddc804b2b31d13e4b5a4e234e1 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -599,6 +599,80 @@ describe User, models: true do
     end
   end
 
+  describe '.search_with_secondary_emails' do
+    def search_with_secondary_emails(query)
+      described_class.search_with_secondary_emails(query)
+    end
+
+    let!(:user) { create(:user) }
+    let!(:email) { create(:email) }
+
+    it 'returns users with a matching name' do
+      expect(search_with_secondary_emails(user.name)).to eq([user])
+    end
+
+    it 'returns users with a partially matching name' do
+      expect(search_with_secondary_emails(user.name[0..2])).to eq([user])
+    end
+
+    it 'returns users with a matching name regardless of the casing' do
+      expect(search_with_secondary_emails(user.name.upcase)).to eq([user])
+    end
+
+    it 'returns users with a matching email' do
+      expect(search_with_secondary_emails(user.email)).to eq([user])
+    end
+
+    it 'returns users with a partially matching email' do
+      expect(search_with_secondary_emails(user.email[0..2])).to eq([user])
+    end
+
+    it 'returns users with a matching email regardless of the casing' do
+      expect(search_with_secondary_emails(user.email.upcase)).to eq([user])
+    end
+
+    it 'returns users with a matching username' do
+      expect(search_with_secondary_emails(user.username)).to eq([user])
+    end
+
+    it 'returns users with a partially matching username' do
+      expect(search_with_secondary_emails(user.username[0..2])).to eq([user])
+    end
+
+    it 'returns users with a matching username regardless of the casing' do
+      expect(search_with_secondary_emails(user.username.upcase)).to eq([user])
+    end
+
+    it 'returns users with a matching whole secondary email' do
+      expect(search_with_secondary_emails(email.email)).to eq([email.user])
+    end
+
+    it 'returns users with a matching part of secondary email' do
+      expect(search_with_secondary_emails(email.email[1..4])).to eq([email.user])
+    end
+
+    it 'return users with a matching part of secondary email regardless of case' do
+      expect(search_with_secondary_emails(email.email[1..4].upcase)).to eq([email.user])
+      expect(search_with_secondary_emails(email.email[1..4].downcase)).to eq([email.user])
+      expect(search_with_secondary_emails(email.email[1..4].capitalize)).to eq([email.user])
+    end
+
+    it 'returns multiple users with matching secondary emails' do
+      email1 = create(:email, email: '1_testemail@example.com')
+      email2 = create(:email, email: '2_testemail@example.com')
+      email3 = create(:email, email: 'other@email.com')
+      email3.user.update_attributes!(email: 'another@mail.com')
+
+      expect(
+        search_with_secondary_emails('testemail@example.com').map(&:id)
+      ).to include(email1.user.id, email2.user.id)
+
+      expect(
+        search_with_secondary_emails('testemail@example.com').map(&:id)
+      ).not_to include(email3.user.id)
+    end
+  end
+
   describe 'by_username_or_id' do
     let(:user1) { create(:user, username: 'foo') }
 
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 658e3c13a73e1a234547d0c53dddcc953aaae4f1..96249a7d8c352a7ef32d6644a6439ea6736855ad 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -6,6 +6,7 @@ describe ProjectPolicy, models: true do
   let(:dev) { create(:user) }
   let(:master) { create(:user) }
   let(:owner) { create(:user) }
+  let(:admin) { create(:admin) }
   let(:project) { create(:empty_project, :public, namespace: owner.namespace) }
 
   let(:guest_permissions) do
@@ -152,6 +153,19 @@ describe ProjectPolicy, models: true do
     context 'owner' do
       let(:current_user) { owner }
 
+      it do
+        is_expected.to include(*guest_permissions)
+        is_expected.to include(*reporter_permissions)
+        is_expected.to include(*team_member_reporter_permissions)
+        is_expected.to include(*developer_permissions)
+        is_expected.to include(*master_permissions)
+        is_expected.to include(*owner_permissions)
+      end
+    end
+
+    context 'admin' do
+      let(:current_user) { admin }
+
       it do
         is_expected.to include(*guest_permissions)
         is_expected.to include(*reporter_permissions)
diff --git a/spec/rake_helper.rb b/spec/rake_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9b5b4bf9feaceca152c63183f92a9583983b6031
--- /dev/null
+++ b/spec/rake_helper.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+require 'rake'
+
+RSpec.configure do |config|
+  config.include RakeHelpers
+
+  # Redirect stdout so specs don't have so much noise
+  config.before(:all) do
+    $stdout = StringIO.new
+
+    Rake.application.rake_require 'tasks/gitlab/task_helpers'
+    Rake::Task.define_task :environment
+  end
+
+  # Reset stdout
+  config.after(:all) do
+    $stdout = STDOUT
+  end
+end
diff --git a/spec/requests/api/api_helpers_spec.rb b/spec/requests/api/api_helpers_spec.rb
index f7fe4c108732ed83bd5749d3e2701bbce5fcd796..01bb9e955e01c4201e4fe75b2524cda398076f9b 100644
--- a/spec/requests/api/api_helpers_spec.rb
+++ b/spec/requests/api/api_helpers_spec.rb
@@ -265,36 +265,6 @@ describe API::Helpers, api: true do
     end
   end
 
-  describe '.to_boolean' do
-    it 'accepts booleans' do
-      expect(to_boolean(true)).to be(true)
-      expect(to_boolean(false)).to be(false)
-    end
-
-    it 'converts a valid string to a boolean' do
-      expect(to_boolean(true)).to be(true)
-      expect(to_boolean('true')).to be(true)
-      expect(to_boolean('YeS')).to be(true)
-      expect(to_boolean('t')).to be(true)
-      expect(to_boolean('1')).to be(true)
-      expect(to_boolean('ON')).to be(true)
-
-      expect(to_boolean('FaLse')).to be(false)
-      expect(to_boolean('F')).to be(false)
-      expect(to_boolean('NO')).to be(false)
-      expect(to_boolean('n')).to be(false)
-      expect(to_boolean('0')).to be(false)
-      expect(to_boolean('oFF')).to be(false)
-    end
-
-    it 'converts an invalid string to nil' do
-      expect(to_boolean('fals')).to be_nil
-      expect(to_boolean('yeah')).to be_nil
-      expect(to_boolean('')).to be_nil
-      expect(to_boolean(nil)).to be_nil
-    end
-  end
-
   describe '.handle_api_exception' do
     before do
       allow_any_instance_of(self.class).to receive(:sentry_enabled?).and_return(true)
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 3ba257256a06e7502b533b51a71c3f54a5b68c32..7b47bf5afc1d9b6d9a538cb583c47a892a245a10 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -37,7 +37,7 @@ describe API::API, api: true  do
       end
     end
 
-    context "when authenticated as  admin" do
+    context "when authenticated as admin" do
       it "admin: returns an array of all groups" do
         get api("/groups", admin)
         expect(response).to have_http_status(200)
@@ -55,6 +55,17 @@ describe API::API, api: true  do
         expect(json_response.length).to eq(1)
       end
     end
+
+    context "when using all_available in request" do
+      it "returns all groups you have access to" do
+        public_group = create :group, :public
+        get api("/groups", user1), all_available: true
+
+        expect(response).to have_http_status(200)
+        expect(json_response).to be_an Array
+        expect(json_response.first['name']).to eq(public_group.name)
+      end
+    end
   end
 
   describe "GET /groups/:id" do
diff --git a/spec/requests/api/merge_request_diffs_spec.rb b/spec/requests/api/merge_request_diffs_spec.rb
index 8f1e5ac98917a5b630113c4ae3cafe1bb332d0fc..131c2d406ea46b7bfd496086539d10b529c2e768 100644
--- a/spec/requests/api/merge_request_diffs_spec.rb
+++ b/spec/requests/api/merge_request_diffs_spec.rb
@@ -14,14 +14,14 @@ describe API::API, 'MergeRequestDiffs', api: true  do
   end
 
   describe 'GET /projects/:id/merge_requests/:merge_request_id/versions' do
-    context 'valid merge request' do
-      before { get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user) }
-      let(:merge_request_diff) { merge_request.merge_request_diffs.first }
-
-      it { expect(response.status).to eq 200 }
-      it { expect(json_response.size).to eq(merge_request.merge_request_diffs.size) }
-      it { expect(json_response.first['id']).to eq(merge_request_diff.id) }
-      it { expect(json_response.first['head_commit_sha']).to eq(merge_request_diff.head_commit_sha) }
+    it 'returns 200 for a valid merge request' do
+      get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user)
+      merge_request_diff = merge_request.merge_request_diffs.first
+
+      expect(response.status).to eq 200
+      expect(json_response.size).to eq(merge_request.merge_request_diffs.size)
+      expect(json_response.first['id']).to eq(merge_request_diff.id)
+      expect(json_response.first['head_commit_sha']).to eq(merge_request_diff.head_commit_sha)
     end
 
     it 'returns a 404 when merge_request_id not found' do
@@ -31,14 +31,14 @@ describe API::API, 'MergeRequestDiffs', api: true  do
   end
 
   describe 'GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id' do
-    context 'valid merge request' do
-      before { get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user) }
-      let(:merge_request_diff) { merge_request.merge_request_diffs.first }
-
-      it { expect(response.status).to eq 200 }
-      it { expect(json_response['id']).to eq(merge_request_diff.id) }
-      it { expect(json_response['head_commit_sha']).to eq(merge_request_diff.head_commit_sha) }
-      it { expect(json_response['diffs'].size).to eq(merge_request_diff.diffs.size) }
+    it 'returns a 200 for a valid merge request' do
+      merge_request_diff = merge_request.merge_request_diffs.first
+      get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user)
+
+      expect(response.status).to eq 200
+      expect(json_response['id']).to eq(merge_request_diff.id)
+      expect(json_response['head_commit_sha']).to eq(merge_request_diff.head_commit_sha)
+      expect(json_response['diffs'].size).to eq(merge_request_diff.diffs.size)
     end
 
     it 'returns a 404 when merge_request_id not found' do
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index b813ee967f81dcc52b397c458c81ef412519772c..bae4fa11ec2e89041e73f67b54ded92324828cbd 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -186,14 +186,14 @@ describe API::API, api: true  do
   end
 
   describe 'GET /projects/:id/merge_requests/:merge_request_id/commits' do
-    context 'valid merge request' do
-      before { get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/commits", user) }
-      let(:commit) { merge_request.commits.first }
-
-      it { expect(response.status).to eq 200 }
-      it { expect(json_response.size).to eq(merge_request.commits.size) }
-      it { expect(json_response.first['id']).to eq(commit.id) }
-      it { expect(json_response.first['title']).to eq(commit.title) }
+    it 'returns a 200 when merge request is valid' do
+      get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/commits", user)
+      commit = merge_request.commits.first
+
+      expect(response.status).to eq 200
+      expect(json_response.size).to eq(merge_request.commits.size)
+      expect(json_response.first['id']).to eq(commit.id)
+      expect(json_response.first['title']).to eq(commit.title)
     end
 
     it 'returns a 404 when merge_request_id not found' do
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index cfcdcad74cd71d7ba833dc2d48bd304ad36816ae..5f39329a1b821c7392d01c81f97c216d4fb108e1 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -88,6 +88,7 @@ describe API::API, 'ProjectHooks', api: true do
       expect do
         post api("/projects/#{project.id}/hooks", user), url: "http://example.com", issues_events: true
       end.to change {project.hooks.count}.by(1)
+
       expect(response).to have_http_status(201)
       expect(json_response['url']).to eq('http://example.com')
       expect(json_response['issues_events']).to eq(true)
@@ -99,6 +100,24 @@ describe API::API, 'ProjectHooks', api: true do
       expect(json_response['pipeline_events']).to eq(false)
       expect(json_response['wiki_page_events']).to eq(false)
       expect(json_response['enable_ssl_verification']).to eq(true)
+      expect(json_response).not_to include('token')
+    end
+
+    it "adds the token without including it in the response" do
+      token = "secret token"
+
+      expect do
+        post api("/projects/#{project.id}/hooks", user), url: "http://example.com", token: token
+      end.to change {project.hooks.count}.by(1)
+
+      expect(response).to have_http_status(201)
+      expect(json_response["url"]).to eq("http://example.com")
+      expect(json_response).not_to include("token")
+
+      hook = project.hooks.find(json_response["id"])
+
+      expect(hook.url).to eq("http://example.com")
+      expect(hook.token).to eq(token)
     end
 
     it "returns a 400 error if url not given" do
@@ -129,6 +148,19 @@ describe API::API, 'ProjectHooks', api: true do
       expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification)
     end
 
+    it "adds the token without including it in the response" do
+      token = "secret token"
+
+      put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: "http://example.org", token: token
+
+      expect(response).to have_http_status(200)
+      expect(json_response["url"]).to eq("http://example.org")
+      expect(json_response).not_to include("token")
+
+      expect(hook.reload.url).to eq("http://example.org")
+      expect(hook.reload.token).to eq(token)
+    end
+
     it "returns 404 error if hook id not found" do
       put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org'
       expect(response).to have_http_status(404)
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index f4903d8e0beeb0a3c8cde79c59985285d119536c..096a8ebab706fe5a20d1214793001226699c5e57 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -33,6 +33,7 @@ describe API::API, 'Settings', api: true  do
         expect(json_response['default_projects_limit']).to eq(3)
         expect(json_response['signin_enabled']).to be_falsey
         expect(json_response['repository_storage']).to eq('custom')
+        expect(json_response['repository_storages']).to eq(['custom'])
         expect(json_response['koding_enabled']).to be_truthy
         expect(json_response['koding_url']).to eq('http://koding.example.com')
       end
diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb
index f8a1aed54419c8bb9b42b8d602af6ef653a70ee3..f685a3685e6969f4779d23fa64478cd2a5656624 100644
--- a/spec/requests/api/system_hooks_spec.rb
+++ b/spec/requests/api/system_hooks_spec.rb
@@ -13,6 +13,7 @@ describe API::API, api: true  do
     context "when no user" do
       it "returns authentication error" do
         get api("/hooks")
+
         expect(response).to have_http_status(401)
       end
     end
@@ -20,6 +21,7 @@ describe API::API, api: true  do
     context "when not an admin" do
       it "returns forbidden error" do
         get api("/hooks", user)
+
         expect(response).to have_http_status(403)
       end
     end
@@ -27,9 +29,12 @@ describe API::API, api: true  do
     context "when authenticated as admin" do
       it "returns an array of hooks" do
         get api("/hooks", admin)
+
         expect(response).to have_http_status(200)
         expect(json_response).to be_an Array
         expect(json_response.first['url']).to eq(hook.url)
+        expect(json_response.first['push_events']).to be true
+        expect(json_response.first['tag_push_events']).to be false
       end
     end
   end
@@ -43,6 +48,7 @@ describe API::API, api: true  do
 
     it "responds with 400 if url not given" do
       post api("/hooks", admin)
+
       expect(response).to have_http_status(400)
     end
 
@@ -51,6 +57,14 @@ describe API::API, api: true  do
         post api("/hooks", admin)
       end.not_to change { SystemHook.count }
     end
+
+    it 'sets default values for events' do
+      post api('/hooks', admin), url: 'http://mep.mep', enable_ssl_verification: true
+
+      expect(response).to have_http_status(201)
+      expect(json_response['enable_ssl_verification']).to be true
+      expect(json_response['tag_push_events']).to be false
+    end
   end
 
   describe "GET /hooks/:id" do
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 7b7d62feb2c8c4bbd5b67aa10e340b7a2c93fae1..6d49c42c21519c1d33b92f4825d97326a0ddf16e 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -220,26 +220,33 @@ describe Ci::API::API do
       end
 
       context 'when request is valid' do
-        it { expect(response.status).to eq 202 }
+        it 'gets correct response' do
+          expect(response.status).to eq 202
+          expect(response.header).to have_key 'Range'
+          expect(response.header).to have_key 'Build-Status'
+        end
+
         it { expect(build.reload.trace).to eq 'BUILD TRACE appended' }
-        it { expect(response.header).to have_key 'Range' }
-        it { expect(response.header).to have_key 'Build-Status' }
       end
 
       context 'when content-range start is too big' do
         let(:headers_with_range) { headers.merge({ 'Content-Range' => '15-20' }) }
 
-        it { expect(response.status).to eq 416 }
-        it { expect(response.header).to have_key 'Range' }
-        it { expect(response.header['Range']).to eq '0-11' }
+        it 'gets correct response' do
+          expect(response.status).to eq 416
+          expect(response.header).to have_key 'Range'
+          expect(response.header['Range']).to eq '0-11'
+        end
       end
 
       context 'when content-range start is too small' do
         let(:headers_with_range) { headers.merge({ 'Content-Range' => '8-20' }) }
 
-        it { expect(response.status).to eq 416 }
-        it { expect(response.header).to have_key 'Range' }
-        it { expect(response.header['Range']).to eq '0-11' }
+        it 'gets correct response' do
+          expect(response.status).to eq 416
+          expect(response.header).to have_key 'Range'
+          expect(response.header['Range']).to eq '0-11'
+        end
       end
 
       context 'when Content-Range header is missing' do
diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb
index 43596f07cb5d138c4b6c7d81a8868fbb958c561b..d6c26fd8a94bb6f426b5d0ef9d512c7e244113d8 100644
--- a/spec/requests/ci/api/runners_spec.rb
+++ b/spec/requests/ci/api/runners_spec.rb
@@ -109,10 +109,12 @@ describe Ci::API::API do
   end
 
   describe "DELETE /runners/delete" do
-    let!(:runner) { FactoryGirl.create(:ci_runner) }
-    before { delete ci_api("/runners/delete"), token: runner.token }
+    it 'returns 200' do
+      runner = FactoryGirl.create(:ci_runner)
+      delete ci_api("/runners/delete"), token: runner.token
 
-    it { expect(response).to have_http_status 200 }
-    it { expect(Ci::Runner.count).to eq(0) }
+      expect(response).to have_http_status 200
+      expect(Ci::Runner.count).to eq(0)
+    end
   end
 end
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index dbdf83a0dff9e9f0f43c78bc4c3cd42300f8adc9..9bfc84c7425186c472ac76cf9886bf428e05d20f 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -284,7 +284,17 @@ describe 'Git LFS API and storage' do
           let(:authorization) { authorize_ci_project }
 
           shared_examples 'can download LFS only from own projects' do
-            context 'for own project' do
+            context 'for owned project' do
+              let(:project) { create(:empty_project, namespace: user.namespace) }
+
+              let(:update_permissions) do
+                project.lfs_objects << lfs_object
+              end
+
+              it_behaves_like 'responds with a file'
+            end
+
+            context 'for member of project' do
               let(:pipeline) { create(:ci_empty_pipeline, project: project) }
 
               let(:update_permissions) do
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index c64df4979b096c29fe630f29d8f2ef596b96cee1..bb26513103d08b0efeddeeec95fdd804cc6788a0 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -245,6 +245,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
 
             it_behaves_like 'a pullable'
           end
+
+          context 'when you are owner' do
+            let(:project) { create(:empty_project, namespace: current_user.namespace) }
+
+            it_behaves_like 'a pullable'
+          end
         end
 
         context 'for private' do
@@ -266,6 +272,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
 
               it_behaves_like 'a pullable'
             end
+
+            context 'when you are owner' do
+              let(:project) { create(:empty_project, namespace: current_user.namespace) }
+
+              it_behaves_like 'a pullable'
+            end
           end
         end
       end
@@ -276,13 +288,21 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
         end
 
         context 'disallow for all' do
-          let(:project) { create(:empty_project, :public) }
+          context 'when you are member' do
+            let(:project) { create(:empty_project, :public) }
 
-          before do
-            project.team << [current_user, :developer]
+            before do
+              project.team << [current_user, :developer]
+            end
+
+            it_behaves_like 'an inaccessible'
           end
 
-          it_behaves_like 'an inaccessible'
+          context 'when you are owner' do
+            let(:project) { create(:empty_project, :public, namespace: current_user.namespace) }
+
+            it_behaves_like 'an inaccessible'
+          end
         end
       end
     end
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0670ac2faa2b943dac8fdd65e396cf7ec49dd5a8
--- /dev/null
+++ b/spec/services/members/create_service_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Members::CreateService, services: true do
+  let(:project) { create(:empty_project) }
+  let(:user) { create(:user) }
+  let(:project_user) { create(:user) }
+
+  before { project.team << [user, :master] }
+
+  it 'adds user to members' do
+    params = { user_ids: project_user.id.to_s, access_level: Gitlab::Access::GUEST }
+    result = described_class.new(project, user, params).execute
+
+    expect(result).to be_truthy
+    expect(project.users).to include project_user
+  end
+
+  it 'adds no user to members' do
+    params = { user_ids: '', access_level: Gitlab::Access::GUEST }
+    result = described_class.new(project, user, params).execute
+
+    expect(result).to be_falsey
+    expect(project.users).not_to include project_user
+  end
+end
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 3a3f07ddcb9ddd332894116d05c45786dfacc1ff..3f5df049ea2aeb4afbebec55bcc872e2ecd66a52 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -25,6 +25,8 @@ describe MergeRequests::BuildService, services: true do
 
   before do
     allow(CompareService).to receive_message_chain(:new, :execute).and_return(compare)
+    allow(project).to receive(:commit).and_return(commit_1)
+    allow(project).to receive(:commit).and_return(commit_2)
   end
 
   describe 'execute' do
@@ -193,5 +195,52 @@ describe MergeRequests::BuildService, services: true do
         end
       end
     end
+
+    context 'source branch does not exist' do
+      before do
+        allow(project).to receive(:commit).with(source_branch).and_return(nil)
+        allow(project).to receive(:commit).with(target_branch).and_return(commit_1)
+      end
+
+      it 'forbids the merge request from being created' do
+        expect(merge_request.can_be_created).to eq(false)
+      end
+
+      it 'adds an error message to the merge request' do
+        expect(merge_request.errors).to contain_exactly('Source branch "feature-branch" does not exist')
+      end
+    end
+
+    context 'target branch does not exist' do
+      before do
+        allow(project).to receive(:commit).with(source_branch).and_return(commit_1)
+        allow(project).to receive(:commit).with(target_branch).and_return(nil)
+      end
+
+      it 'forbids the merge request from being created' do
+        expect(merge_request.can_be_created).to eq(false)
+      end
+
+      it 'adds an error message to the merge request' do
+        expect(merge_request.errors).to contain_exactly('Target branch "master" does not exist')
+      end
+    end
+
+    context 'both source and target branches do not exist' do
+      before do
+        allow(project).to receive(:commit).and_return(nil)
+      end
+
+      it 'forbids the merge request from being created' do
+        expect(merge_request.can_be_created).to eq(false)
+      end
+
+      it 'adds both error messages to the merge request' do
+        expect(merge_request.errors).to contain_exactly(
+          'Source branch "feature-branch" does not exist',
+          'Target branch "master" does not exist'
+        )
+      end
+    end
   end
 end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 3ea1273abc3ff3875a83b8b8c06ce71e87dad6a9..876bfaf085c77a3235460de6f298010a7e316321 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -69,7 +69,7 @@ describe Projects::CreateService, services: true do
 
       context 'wiki_enabled false does not create wiki repository directory' do
         before do
-          @opts.merge!( { project_feature_attributes: { wiki_access_level: ProjectFeature::DISABLED } })
+          @opts.merge!(wiki_enabled: false)
           @project = create_project(@user, @opts)
           @path = ProjectWiki.new(@project, @user).send(:path_to_repo)
         end
diff --git a/spec/support/banzai/reference_filter_shared_examples.rb b/spec/support/banzai/reference_filter_shared_examples.rb
new file mode 100644
index 0000000000000000000000000000000000000000..eb5da662ab5f9c26aee739fcf971337ee818476b
--- /dev/null
+++ b/spec/support/banzai/reference_filter_shared_examples.rb
@@ -0,0 +1,13 @@
+# Specs for reference links containing HTML.
+#
+# Requires a reference:
+#   let(:reference) { '#42' }
+shared_examples 'a reference containing an element node' do
+  let(:inner_html) { 'element <code>node</code> inside' }
+  let(:reference_with_element) { %{<a href="#{reference}">#{inner_html}</a>} }
+
+  it 'does not escape inner html' do
+    doc = reference_filter(reference_with_element)
+    expect(doc.children.first.inner_html).to eq(inner_html)
+  end
+end
diff --git a/spec/support/import_export/common_util.rb b/spec/support/import_export/common_util.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2542a59bb00d019f0b4502e31e0c3193d4f32393
--- /dev/null
+++ b/spec/support/import_export/common_util.rb
@@ -0,0 +1,10 @@
+module ImportExport
+  module CommonUtil
+    def setup_symlink(tmpdir, symlink_name)
+      allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(tmpdir)
+
+      File.open("#{tmpdir}/test", 'w') { |file| file.write("test") }
+      FileUtils.ln_s("#{tmpdir}/test", "#{tmpdir}/#{symlink_name}")
+    end
+  end
+end
diff --git a/spec/support/rake_helpers.rb b/spec/support/rake_helpers.rb
new file mode 100644
index 0000000000000000000000000000000000000000..52d80c698355e26c40b823bc0d1eb558b6ef079a
--- /dev/null
+++ b/spec/support/rake_helpers.rb
@@ -0,0 +1,10 @@
+module RakeHelpers
+  def run_rake_task(task_name)
+    Rake::Task[task_name].reenable
+    Rake.application.invoke_task task_name
+  end
+
+  def stub_warn_user_is_not_gitlab
+    allow_any_instance_of(Object).to receive(:warn_user_is_not_gitlab)
+  end
+end
diff --git a/spec/tasks/gitlab/shell_rake_spec.rb b/spec/tasks/gitlab/shell_rake_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..226d34fe2c98bc6a6539033c558be61e97469135
--- /dev/null
+++ b/spec/tasks/gitlab/shell_rake_spec.rb
@@ -0,0 +1,26 @@
+require 'rake_helper'
+
+describe 'gitlab:shell rake tasks' do
+  before do
+    Rake.application.rake_require 'tasks/gitlab/shell'
+
+    stub_warn_user_is_not_gitlab
+  end
+
+  describe 'install task' do
+    it 'invokes create_hooks task' do
+      expect(Rake::Task['gitlab:shell:create_hooks']).to receive(:invoke)
+
+      run_rake_task('gitlab:shell:install')
+    end
+  end
+
+  describe 'create_hooks task' do
+    it 'calls gitlab-shell bin/create_hooks' do
+      expect_any_instance_of(Object).to receive(:system)
+        .with("#{Gitlab.config.gitlab_shell.path}/bin/create-hooks", *repository_storage_paths_args)
+
+      run_rake_task('gitlab:shell:create_hooks')
+    end
+  end
+end
diff --git a/vendor/assets/javascripts/Sortable.js b/vendor/assets/javascripts/Sortable.js
index eca7c5012b2b1178f4fd942e95676555344ea208..f9e57bcb855dc58b796620a477d0c8a03d2e4624 100644
--- a/vendor/assets/javascripts/Sortable.js
+++ b/vendor/assets/javascripts/Sortable.js
@@ -4,8 +4,7 @@
  * @license MIT
  */
 
-
-(function (factory) {
+(function sortableModule(factory) {
 	"use strict";
 
 	if (typeof define === "function" && define.amd) {
@@ -15,15 +14,22 @@
 		module.exports = factory();
 	}
 	else if (typeof Package !== "undefined") {
+		//noinspection JSUnresolvedVariable
 		Sortable = factory();  // export for Meteor.js
 	}
 	else {
 		/* jshint sub:true */
 		window["Sortable"] = factory();
 	}
-})(function () {
+})(function sortableFactory() {
 	"use strict";
 
+	if (typeof window == "undefined" || !window.document) {
+		return function sortableError() {
+			throw new Error("Sortable.js requires a window with a document");
+		};
+	}
+
 	var dragEl,
 		parentEl,
 		ghostEl,
@@ -33,6 +39,7 @@
 
 		scrollEl,
 		scrollParentEl,
+		scrollCustomFn,
 
 		lastEl,
 		lastCSS,
@@ -42,6 +49,8 @@
 		newIndex,
 
 		activeGroup,
+		putSortable,
+
 		autoScroll = {},
 
 		tapEvt,
@@ -58,8 +67,15 @@
 		document = win.document,
 		parseInt = win.parseInt,
 
+		$ = win.jQuery || win.Zepto,
+		Polymer = win.Polymer,
+
 		supportDraggable = !!('draggable' in document.createElement('div')),
 		supportCssPointerEvents = (function (el) {
+			// false when IE11
+			if (!!navigator.userAgent.match(/Trident.*rv[ :]?11\./)) {
+				return false;
+			}
 			el = document.createElement('x');
 			el.style.cssText = 'pointer-events:auto';
 			return el.style.pointerEvents === 'auto';
@@ -88,13 +104,17 @@
 					winHeight = window.innerHeight,
 
 					vx,
-					vy
+					vy,
+
+					scrollOffsetX,
+					scrollOffsetY
 				;
 
 				// Delect scrollEl
 				if (scrollParentEl !== rootEl) {
 					scrollEl = options.scroll;
 					scrollParentEl = rootEl;
+					scrollCustomFn = options.scrollFn;
 
 					if (scrollEl === true) {
 						scrollEl = rootEl;
@@ -136,11 +156,18 @@
 
 					if (el) {
 						autoScroll.pid = setInterval(function () {
+							scrollOffsetY = vy ? vy * speed : 0;
+							scrollOffsetX = vx ? vx * speed : 0;
+
+							if ('function' === typeof(scrollCustomFn)) {
+								return scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt);
+							}
+
 							if (el === win) {
-								win.scrollTo(win.pageXOffset + vx * speed, win.pageYOffset + vy * speed);
+								win.scrollTo(win.pageXOffset + scrollOffsetX, win.pageYOffset + scrollOffsetY);
 							} else {
-								vy && (el.scrollTop += vy * speed);
-								vx && (el.scrollLeft += vx * speed);
+								el.scrollTop += scrollOffsetY;
+								el.scrollLeft += scrollOffsetX;
 							}
 						}, 24);
 					}
@@ -149,19 +176,39 @@
 		}, 30),
 
 		_prepareGroup = function (options) {
-			var group = options.group;
+			function toFn(value, pull) {
+				if (value === void 0 || value === true) {
+					value = group.name;
+				}
 
-			if (!group || typeof group != 'object') {
-				group = options.group = {name: group};
+				if (typeof value === 'function') {
+					return value;
+				} else {
+					return function (to, from) {
+						var fromGroup = from.options.group.name;
+
+						return pull
+							? value
+							: value && (value.join
+								? value.indexOf(fromGroup) > -1
+								: (fromGroup == value)
+							);
+					};
+				}
 			}
 
-			['pull', 'put'].forEach(function (key) {
-				if (!(key in group)) {
-					group[key] = true;
-				}
-			});
+			var group = {};
+			var originalGroup = options.group;
+
+			if (!originalGroup || typeof originalGroup != 'object') {
+				originalGroup = {name: originalGroup};
+			}
 
-			options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' ';
+			group.name = originalGroup.name;
+			group.checkPull = toFn(originalGroup.pull, true);
+			group.checkPut = toFn(originalGroup.put);
+
+			options.group = group;
 		}
 	;
 
@@ -198,6 +245,7 @@
 			draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
 			ghostClass: 'sortable-ghost',
 			chosenClass: 'sortable-chosen',
+			dragClass: 'sortable-drag',
 			ignore: 'a, img',
 			filter: null,
 			animation: 0,
@@ -211,7 +259,8 @@
 			forceFallback: false,
 			fallbackClass: 'sortable-fallback',
 			fallbackOnBody: false,
-			fallbackTolerance: 0
+			fallbackTolerance: 0,
+			fallbackOffset: {x: 0, y: 0}
 		};
 
 
@@ -224,7 +273,7 @@
 
 		// Bind all private methods
 		for (var fn in this) {
-			if (fn.charAt(0) === '_') {
+			if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
 				this[fn] = this[fn].bind(this);
 			}
 		}
@@ -258,7 +307,7 @@
 				type = evt.type,
 				touch = evt.touches && evt.touches[0],
 				target = (touch || evt).target,
-				originalTarget = target,
+				originalTarget = evt.target.shadowRoot && evt.path[0] || target,
 				filter = options.filter,
 				startIndex;
 
@@ -271,13 +320,13 @@
 				return; // only left button or enabled
 			}
 
-			target = _closest(target, options.draggable, el);
-
-			if (!target) {
+			if (options.handle && !_closest(originalTarget, options.handle, el)) {
 				return;
 			}
 
-			if (options.handle && !_closest(originalTarget, options.handle, el)) {
+			target = _closest(target, options.draggable, el);
+
+			if (!target) {
 				return;
 			}
 
@@ -332,16 +381,18 @@
 				this._lastX = (touch || evt).clientX;
 				this._lastY = (touch || evt).clientY;
 
+				dragEl.style['will-change'] = 'transform';
+
 				dragStartFn = function () {
 					// Delayed drag has been triggered
 					// we can re-enable the events: touchmove/mousemove
 					_this._disableDelayedDrag();
 
 					// Make the element draggable
-					dragEl.draggable = true;
+					dragEl.draggable = _this.nativeDraggable;
 
 					// Chosen item
-					_toggleClass(dragEl, _this.options.chosenClass, true);
+					_toggleClass(dragEl, options.chosenClass, true);
 
 					// Bind the events: dragstart/dragend
 					_this._triggerDragStart(touch);
@@ -408,7 +459,10 @@
 
 			try {
 				if (document.selection) {
-					document.selection.empty();
+					// Timeout neccessary for IE9
+					setTimeout(function () {
+						document.selection.empty();
+					});
 				} else {
 					window.getSelection().removeAllRanges();
 				}
@@ -418,8 +472,11 @@
 
 		_dragStarted: function () {
 			if (rootEl && dragEl) {
+				var options = this.options;
+
 				// Apply effect
-				_toggleClass(dragEl, this.options.ghostClass, true);
+				_toggleClass(dragEl, options.ghostClass, true);
+				_toggleClass(dragEl, options.dragClass, false);
 
 				Sortable.active = this;
 
@@ -443,12 +500,11 @@
 
 				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
 					parent = target,
-					groupName = ' ' + this.options.group.name + '',
 					i = touchDragOverListeners.length;
 
 				if (parent) {
 					do {
-						if (parent[expando] && parent[expando].options.groups.indexOf(groupName) > -1) {
+						if (parent[expando]) {
 							while (i--) {
 								touchDragOverListeners[i]({
 									clientX: touchEvt.clientX,
@@ -478,9 +534,10 @@
 			if (tapEvt) {
 				var	options = this.options,
 					fallbackTolerance = options.fallbackTolerance,
+					fallbackOffset = options.fallbackOffset,
 					touch = evt.touches ? evt.touches[0] : evt,
-					dx = touch.clientX - tapEvt.clientX,
-					dy = touch.clientY - tapEvt.clientY,
+					dx = (touch.clientX - tapEvt.clientX) + fallbackOffset.x,
+					dy = (touch.clientY - tapEvt.clientY) + fallbackOffset.y,
 					translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
 
 				// only set the status to dragging, when we are actually dragging
@@ -520,6 +577,7 @@
 
 				_toggleClass(ghostEl, options.ghostClass, false);
 				_toggleClass(ghostEl, options.fallbackClass, true);
+				_toggleClass(ghostEl, options.dragClass, true);
 
 				_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
 				_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
@@ -545,13 +603,15 @@
 
 			this._offUpEvents();
 
-			if (activeGroup.pull == 'clone') {
-				cloneEl = dragEl.cloneNode(true);
+			if (activeGroup.checkPull(this, this, dragEl, evt) == 'clone') {
+				cloneEl = _clone(dragEl);
 				_css(cloneEl, 'display', 'none');
 				rootEl.insertBefore(cloneEl, dragEl);
 				_dispatchEvent(this, rootEl, 'clone', dragEl);
 			}
 
+			_toggleClass(dragEl, options.dragClass, true);
+
 			if (useFallback) {
 				if (useFallback === 'touch') {
 					// Bind touch events
@@ -581,10 +641,11 @@
 			var el = this.el,
 				target,
 				dragRect,
+				targetRect,
 				revert,
 				options = this.options,
 				group = options.group,
-				groupPut = group.put,
+				activeSortable = Sortable.active,
 				isOwner = (activeGroup === group),
 				canSort = options.sort;
 
@@ -598,9 +659,9 @@
 			if (activeGroup && !options.disabled &&
 				(isOwner
 					? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
-					: activeGroup.pull && groupPut && (
-						(activeGroup.name === group.name) || // by Name
-						(groupPut.indexOf && ~groupPut.indexOf(activeGroup.name)) // by Array
+					: (
+						putSortable === this ||
+						activeGroup.checkPull(this, activeSortable, dragEl, evt) && group.checkPut(this, activeSortable, dragEl, evt)
 					)
 				) &&
 				(evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
@@ -614,6 +675,7 @@
 
 				target = _closest(evt.target, options.draggable, el);
 				dragRect = dragEl.getBoundingClientRect();
+				putSortable = this;
 
 				if (revert) {
 					_cloneHide(true);
@@ -633,7 +695,6 @@
 				if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
 					(el === evt.target) && (target = _ghostIsLast(el, evt))
 				) {
-
 					if (target) {
 						if (target.animated) {
 							return;
@@ -644,7 +705,7 @@
 
 					_cloneHide(isOwner);
 
-					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) {
+					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt) !== false) {
 						if (!dragEl.contains(el)) {
 							el.appendChild(dragEl);
 							parentEl = el; // actualization
@@ -661,9 +722,9 @@
 						lastParentCSS = _css(target.parentNode);
 					}
 
+					targetRect = target.getBoundingClientRect();
 
-					var targetRect = target.getBoundingClientRect(),
-						width = targetRect.right - targetRect.left,
+					var width = targetRect.right - targetRect.left,
 						height = targetRect.bottom - targetRect.top,
 						floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display)
 							|| (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
@@ -671,7 +732,7 @@
 						isLong = (target.offsetHeight > dragEl.offsetHeight),
 						halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
 						nextSibling = target.nextElementSibling,
-						moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect),
+						moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt),
 						after
 					;
 
@@ -784,6 +845,7 @@
 					}
 
 					_disableDraggable(dragEl);
+					dragEl.style['will-change'] = '';
 
 					// Remove class's
 					_toggleClass(dragEl, this.options.ghostClass, false);
@@ -793,15 +855,16 @@
 						newIndex = _index(dragEl, options.draggable);
 
 						if (newIndex >= 0) {
-							// drag from one list and drop into another
-							_dispatchEvent(null, parentEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
-							_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
 
 							// Add event
 							_dispatchEvent(null, parentEl, 'add', dragEl, rootEl, oldIndex, newIndex);
 
 							// Remove event
 							_dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
+
+							// drag from one list and drop into another
+							_dispatchEvent(null, parentEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
+							_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
 						}
 					}
 					else {
@@ -821,7 +884,8 @@
 					}
 
 					if (Sortable.active) {
-						if (newIndex === null || newIndex === -1) {
+						/* jshint eqnull:true */
+						if (newIndex == null || newIndex === -1) {
 							newIndex = oldIndex;
 						}
 
@@ -837,7 +901,7 @@
 			this._nulling();
 		},
 
-		_nulling: function () {
+		_nulling: function() {
 			rootEl =
 			dragEl =
 			parentEl =
@@ -857,6 +921,7 @@
 			lastEl =
 			lastCSS =
 
+			putSortable =
 			activeGroup =
 			Sortable.active = null;
 		},
@@ -1011,14 +1076,21 @@
 				if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector)) {
 					return el;
 				}
-			}
-			while (el !== ctx && (el = el.parentNode));
+				/* jshint boss:true */
+			} while (el = _getParentOrHost(el));
 		}
 
 		return null;
 	}
 
 
+	function _getParentOrHost(el) {
+		var parent = el.host;
+
+		return (parent && parent.nodeType) ? parent : el.parentNode;
+	}
+
+
 	function _globalDragOver(/**Event*/evt) {
 		if (evt.dataTransfer) {
 			evt.dataTransfer.dropEffect = 'move';
@@ -1094,8 +1166,10 @@
 
 
 	function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) {
+		sortable = (sortable || rootEl[expando]);
+
 		var evt = document.createEvent('Event'),
-			options = (sortable || rootEl[expando]).options,
+			options = sortable.options,
 			onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);
 
 		evt.initEvent(name, true, true);
@@ -1116,7 +1190,7 @@
 	}
 
 
-	function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect) {
+	function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt) {
 		var evt,
 			sortable = fromEl[expando],
 			onMoveFn = sortable.options.onMove,
@@ -1135,7 +1209,7 @@
 		fromEl.dispatchEvent(evt);
 
 		if (onMoveFn) {
-			retVal = onMoveFn.call(sortable, evt);
+			retVal = onMoveFn.call(sortable, evt, originalEvt);
 		}
 
 		return retVal;
@@ -1155,9 +1229,14 @@
 	/** @returns {HTMLElement|false} */
 	function _ghostIsLast(el, evt) {
 		var lastEl = el.lastElementChild,
-				rect = lastEl.getBoundingClientRect();
-
-		return ((evt.clientY - (rect.top + rect.height) > 5) || (evt.clientX - (rect.right + rect.width) > 5)) && lastEl; // min delta
+			rect = lastEl.getBoundingClientRect();
+
+		// 5 — min delta
+		// abs — нельзя добавлять, а то глюки при наведении сверху
+		return (
+			(evt.clientY - (rect.top + rect.height) > 5) ||
+			(evt.clientX - (rect.right + rect.width) > 5)
+		) && lastEl;
 	}
 
 
@@ -1251,6 +1330,15 @@
 		return dst;
 	}
 
+	function _clone(el) {
+		return $
+			? $(el).clone(true)[0]
+			: (Polymer && Polymer.dom
+				? Polymer.dom(el).cloneNode(true)
+				: el.cloneNode(true)
+			);
+	}
+
 
 	// Export utils
 	Sortable.utils = {
@@ -1265,6 +1353,7 @@
 		throttle: _throttle,
 		closest: _closest,
 		toggleClass: _toggleClass,
+		clone: _clone,
 		index: _index
 	};
 
diff --git a/app/assets/javascripts/lib/utils/jquery.timeago.js b/vendor/assets/javascripts/jquery.timeago.js
similarity index 100%
rename from app/assets/javascripts/lib/utils/jquery.timeago.js
rename to vendor/assets/javascripts/jquery.timeago.js
diff --git a/vendor/gitignore/Android.gitignore b/vendor/gitignore/Android.gitignore
index e5df7b9150e27e777ab823d908906e0adbcd8bc0..935ceef0680bdc5d33b9be9117d60300098a8b51 100644
--- a/vendor/gitignore/Android.gitignore
+++ b/vendor/gitignore/Android.gitignore
@@ -39,3 +39,6 @@ captures/
 
 # Keystore files
 *.jks
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
diff --git a/vendor/gitignore/C.gitignore b/vendor/gitignore/C.gitignore
index 7a065c709c75460a6cd3cbc49f58b263a6ad1567..8a365b3d82974d95aca80a3b845a7b7b3527e5c4 100644
--- a/vendor/gitignore/C.gitignore
+++ b/vendor/gitignore/C.gitignore
@@ -7,6 +7,11 @@
 *.obj
 *.elf
 
+# Linker output
+*.ilk
+*.map
+*.exp
+
 # Precompiled Headers
 *.gch
 *.pch
@@ -34,3 +39,13 @@
 # Debug files
 *.dSYM/
 *.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
diff --git a/vendor/gitignore/ExtJs.gitignore b/vendor/gitignore/ExtJs.gitignore
index 5ffc21546ec4c944bbac2c25e6566b83dbb5cb76..c92aea0fe0cfa2342e6e13af1b79b73c1930259b 100644
--- a/vendor/gitignore/ExtJs.gitignore
+++ b/vendor/gitignore/ExtJs.gitignore
@@ -1,4 +1,12 @@
 .architect
+bootstrap.css
+bootstrap.js
 bootstrap.json
+bootstrap.jsonp
 build/
+classic.json
+classic.jsonp
 ext/
+modern.json
+modern.jsonp
+resources/sass/.sass-cache/
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
index ea83a5eb620684a6c672e78fd2f0fe66d3410fd9..0a254147875173d9bf25903d5e1d837b6cdd3561 100644
--- a/vendor/gitignore/Global/JetBrains.gitignore
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -4,9 +4,6 @@
 # User-specific stuff:
 .idea/workspace.xml
 .idea/tasks.xml
-.idea/dictionaries
-.idea/vcs.xml
-.idea/jsLibraryMappings.xml
 
 # Sensitive or high-churn files:
 .idea/dataSources.ids
diff --git a/vendor/gitignore/Global/macOS.gitignore b/vendor/gitignore/Global/macOS.gitignore
index 828a509a137632e51c973018cc379fa1c4aa5be5..f0f3fbc06c89c36533576e61b30e8cd6ca947b41 100644
--- a/vendor/gitignore/Global/macOS.gitignore
+++ b/vendor/gitignore/Global/macOS.gitignore
@@ -1,26 +1,26 @@
-*.DS_Store
-.AppleDouble
-.LSOverride
-
-# Icon must end with two \r
-Icon
-
-
-# Thumbnails
-._*
-
-# Files that might appear in the root of a volume
-.DocumentRevisions-V100
-.fseventsd
-.Spotlight-V100
-.TemporaryItems
-.Trashes
-.VolumeIcon.icns
-.com.apple.timemachine.donotpresent
-
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
+*.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
diff --git a/vendor/gitignore/LICENSE b/vendor/gitignore/LICENSE
index 0e259d42c996742e9e3cba14c677129b2c1b6311..670154e3538863b2d9891fd5483160fbdfc89164 100644
--- a/vendor/gitignore/LICENSE
+++ b/vendor/gitignore/LICENSE
@@ -1,121 +1,116 @@
-Creative Commons Legal Code
-
 CC0 1.0 Universal
 
-    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
-    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
-    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
-    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
-    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
-    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
-    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
-    HEREUNDER.
-
 Statement of Purpose
 
 The laws of most jurisdictions throughout the world automatically confer
-exclusive Copyright and Related Rights (defined below) upon the creator
-and subsequent owner(s) (each and all, an "owner") of an original work of
+exclusive Copyright and Related Rights (defined below) upon the creator and
+subsequent owner(s) (each and all, an "owner") of an original work of
 authorship and/or a database (each, a "Work").
 
-Certain owners wish to permanently relinquish those rights to a Work for
-the purpose of contributing to a commons of creative, cultural and
-scientific works ("Commons") that the public can reliably and without fear
-of later claims of infringement build upon, modify, incorporate in other
-works, reuse and redistribute as freely as possible in any form whatsoever
-and for any purposes, including without limitation commercial purposes.
-These owners may contribute to the Commons to promote the ideal of a free
-culture and the further production of creative, cultural and scientific
-works, or to gain reputation or greater distribution for their Work in
-part through the use and efforts of others.
-
-For these and/or other purposes and motivations, and without any
-expectation of additional consideration or compensation, the person
-associating CC0 with a Work (the "Affirmer"), to the extent that he or she
-is an owner of Copyright and Related Rights in the Work, voluntarily
-elects to apply CC0 to the Work and publicly distribute the Work under its
-terms, with knowledge of his or her Copyright and Related Rights in the
-Work and the meaning and intended legal effect of CC0 on those rights.
+Certain owners wish to permanently relinquish those rights to a Work for the
+purpose of contributing to a commons of creative, cultural and scientific
+works ("Commons") that the public can reliably and without fear of later
+claims of infringement build upon, modify, incorporate in other works, reuse
+and redistribute as freely as possible in any form whatsoever and for any
+purposes, including without limitation commercial purposes. These owners may
+contribute to the Commons to promote the ideal of a free culture and the
+further production of creative, cultural and scientific works, or to gain
+reputation or greater distribution for their Work in part through the use and
+efforts of others.
+
+For these and/or other purposes and motivations, and without any expectation
+of additional consideration or compensation, the person associating CC0 with a
+Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
+and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
+and publicly distribute the Work under its terms, with knowledge of his or her
+Copyright and Related Rights in the Work and the meaning and intended legal
+effect of CC0 on those rights.
 
 1. Copyright and Related Rights. A Work made available under CC0 may be
 protected by copyright and related or neighboring rights ("Copyright and
-Related Rights"). Copyright and Related Rights include, but are not
-limited to, the following:
-
-  i. the right to reproduce, adapt, distribute, perform, display,
-     communicate, and translate a Work;
- ii. moral rights retained by the original author(s) and/or performer(s);
-iii. publicity and privacy rights pertaining to a person's image or
-     likeness depicted in a Work;
- iv. rights protecting against unfair competition in regards to a Work,
-     subject to the limitations in paragraph 4(a), below;
-  v. rights protecting the extraction, dissemination, use and reuse of data
-     in a Work;
- vi. database rights (such as those arising under Directive 96/9/EC of the
-     European Parliament and of the Council of 11 March 1996 on the legal
-     protection of databases, and under any national implementation
-     thereof, including any amended or successor version of such
-     directive); and
-vii. other similar, equivalent or corresponding rights throughout the
-     world based on applicable law or treaty, and any national
-     implementations thereof.
-
-2. Waiver. To the greatest extent permitted by, but not in contravention
-of, applicable law, Affirmer hereby overtly, fully, permanently,
-irrevocably and unconditionally waives, abandons, and surrenders all of
-Affirmer's Copyright and Related Rights and associated claims and causes
-of action, whether now known or unknown (including existing as well as
-future claims and causes of action), in the Work (i) in all territories
-worldwide, (ii) for the maximum duration provided by applicable law or
-treaty (including future time extensions), (iii) in any current or future
-medium and for any number of copies, and (iv) for any purpose whatsoever,
-including without limitation commercial, advertising or promotional
-purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
-member of the public at large and to the detriment of Affirmer's heirs and
-successors, fully intending that such Waiver shall not be subject to
-revocation, rescission, cancellation, termination, or any other legal or
-equitable action to disrupt the quiet enjoyment of the Work by the public
-as contemplated by Affirmer's express Statement of Purpose.
-
-3. Public License Fallback. Should any part of the Waiver for any reason
-be judged legally invalid or ineffective under applicable law, then the
-Waiver shall be preserved to the maximum extent permitted taking into
-account Affirmer's express Statement of Purpose. In addition, to the
-extent the Waiver is so judged Affirmer hereby grants to each affected
-person a royalty-free, non transferable, non sublicensable, non exclusive,
-irrevocable and unconditional license to exercise Affirmer's Copyright and
-Related Rights in the Work (i) in all territories worldwide, (ii) for the
-maximum duration provided by applicable law or treaty (including future
-time extensions), (iii) in any current or future medium and for any number
-of copies, and (iv) for any purpose whatsoever, including without
-limitation commercial, advertising or promotional purposes (the
-"License"). The License shall be deemed effective as of the date CC0 was
-applied by Affirmer to the Work. Should any part of the License for any
-reason be judged legally invalid or ineffective under applicable law, such
-partial invalidity or ineffectiveness shall not invalidate the remainder
-of the License, and in such case Affirmer hereby affirms that he or she
-will not (i) exercise any of his or her remaining Copyright and Related
-Rights in the Work or (ii) assert any associated claims and causes of
-action with respect to the Work, in either case contrary to Affirmer's
-express Statement of Purpose.
+Related Rights"). Copyright and Related Rights include, but are not limited
+to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display, communicate,
+  and translate a Work;
+
+  ii. moral rights retained by the original author(s) and/or performer(s);
+
+  iii. publicity and privacy rights pertaining to a person's image or likeness
+  depicted in a Work;
+
+  iv. rights protecting against unfair competition in regards to a Work,
+  subject to the limitations in paragraph 4(a), below;
+
+  v. rights protecting the extraction, dissemination, use and reuse of data in
+  a Work;
+
+  vi. database rights (such as those arising under Directive 96/9/EC of the
+  European Parliament and of the Council of 11 March 1996 on the legal
+  protection of databases, and under any national implementation thereof,
+  including any amended or successor version of such directive); and
+
+  vii. other similar, equivalent or corresponding rights throughout the world
+  based on applicable law or treaty, and any national implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention of,
+applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
+unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
+and Related Rights and associated claims and causes of action, whether now
+known or unknown (including existing as well as future claims and causes of
+action), in the Work (i) in all territories worldwide, (ii) for the maximum
+duration provided by applicable law or treaty (including future time
+extensions), (iii) in any current or future medium and for any number of
+copies, and (iv) for any purpose whatsoever, including without limitation
+commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
+the Waiver for the benefit of each member of the public at large and to the
+detriment of Affirmer's heirs and successors, fully intending that such Waiver
+shall not be subject to revocation, rescission, cancellation, termination, or
+any other legal or equitable action to disrupt the quiet enjoyment of the Work
+by the public as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason be
+judged legally invalid or ineffective under applicable law, then the Waiver
+shall be preserved to the maximum extent permitted taking into account
+Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
+is so judged Affirmer hereby grants to each affected person a royalty-free,
+non transferable, non sublicensable, non exclusive, irrevocable and
+unconditional license to exercise Affirmer's Copyright and Related Rights in
+the Work (i) in all territories worldwide, (ii) for the maximum duration
+provided by applicable law or treaty (including future time extensions), (iii)
+in any current or future medium and for any number of copies, and (iv) for any
+purpose whatsoever, including without limitation commercial, advertising or
+promotional purposes (the "License"). The License shall be deemed effective as
+of the date CC0 was applied by Affirmer to the Work. Should any part of the
+License for any reason be judged legally invalid or ineffective under
+applicable law, such partial invalidity or ineffectiveness shall not
+invalidate the remainder of the License, and in such case Affirmer hereby
+affirms that he or she will not (i) exercise any of his or her remaining
+Copyright and Related Rights in the Work or (ii) assert any associated claims
+and causes of action with respect to the Work, in either case contrary to
+Affirmer's express Statement of Purpose.
 
 4. Limitations and Disclaimers.
 
- a. No trademark or patent rights held by Affirmer are waived, abandoned,
-    surrendered, licensed or otherwise affected by this document.
- b. Affirmer offers the Work as-is and makes no representations or
-    warranties of any kind concerning the Work, express, implied,
-    statutory or otherwise, including without limitation warranties of
-    title, merchantability, fitness for a particular purpose, non
-    infringement, or the absence of latent or other defects, accuracy, or
-    the present or absence of errors, whether or not discoverable, all to
-    the greatest extent permissible under applicable law.
- c. Affirmer disclaims responsibility for clearing rights of other persons
-    that may apply to the Work or any use thereof, including without
-    limitation any person's Copyright and Related Rights in the Work.
-    Further, Affirmer disclaims responsibility for obtaining any necessary
-    consents, permissions or other rights required for any use of the
-    Work.
- d. Affirmer understands and acknowledges that Creative Commons is not a
-    party to this document and has no duty or obligation with respect to
-    this CC0 or use of the Work.
+  a. No trademark or patent rights held by Affirmer are waived, abandoned,
+  surrendered, licensed or otherwise affected by this document.
+
+  b. Affirmer offers the Work as-is and makes no representations or warranties
+  of any kind concerning the Work, express, implied, statutory or otherwise,
+  including without limitation warranties of title, merchantability, fitness
+  for a particular purpose, non infringement, or the absence of latent or
+  other defects, accuracy, or the present or absence of errors, whether or not
+  discoverable, all to the greatest extent permissible under applicable law.
+
+  c. Affirmer disclaims responsibility for clearing rights of other persons
+  that may apply to the Work or any use thereof, including without limitation
+  any person's Copyright and Related Rights in the Work. Further, Affirmer
+  disclaims responsibility for obtaining any necessary consents, permissions
+  or other rights required for any use of the Work.
+
+  d. Affirmer understands and acknowledges that Creative Commons is not a
+  party to this document and has no duty or obligation with respect to this
+  CC0 or use of the Work.
+
+For more information, please see
+<http://creativecommons.org/publicdomain/zero/1.0/>
diff --git a/vendor/gitignore/Laravel.gitignore b/vendor/gitignore/Laravel.gitignore
index 1cd717b6921f78825776b993f42a451c5fed5a83..e7c594fa3e28d5dc5e1ce08c936b456023c61c09 100644
--- a/vendor/gitignore/Laravel.gitignore
+++ b/vendor/gitignore/Laravel.gitignore
@@ -7,6 +7,7 @@ app/storage/
 
 # Laravel 5 & Lumen specific
 bootstrap/cache/
+public/storage
 .env.*.php
 .env.php
 .env
diff --git a/vendor/gitignore/Nanoc.gitignore b/vendor/gitignore/Nanoc.gitignore
index abc21828a3ed1662bb28af14afb92b5b7efccb4c..3f36ea2a878791caaaba5a67449af08b774275b3 100644
--- a/vendor/gitignore/Nanoc.gitignore
+++ b/vendor/gitignore/Nanoc.gitignore
@@ -1,6 +1,6 @@
-# For projects using nanoc (http://nanoc.ws/)
+# For projects using Nanoc (http://nanoc.ws/)
 
-# Default location for output, needs to match output_dir's value found in config.yaml
+# Default location for output (needs to match output_dir's value found in nanoc.yaml)
 output/
 
 # Temporary file directory
diff --git a/vendor/gitignore/OpenCart.gitignore b/vendor/gitignore/OpenCart.gitignore
index 28e45aa6aac4a1f9fffe82ad52e4169bbd1a25f0..97be41faa387f53ec974ea83d42321af7c65e901 100644
--- a/vendor/gitignore/OpenCart.gitignore
+++ b/vendor/gitignore/OpenCart.gitignore
@@ -11,3 +11,10 @@ system/cache/
 system/logs/
 
 system/storage/
+
+# vQmod log files
+vqmod/logs/*
+# vQmod cache files
+vqmod/vqcache/*
+vqmod/checked.cache
+vqmod/mods.cache
diff --git a/vendor/gitignore/Python.gitignore b/vendor/gitignore/Python.gitignore
index 37fc9d40817580a7922e9f71772922e58465b2b1..6a2bf47ade9c4dd6746a6ceaee6b68c8ce1ce883 100644
--- a/vendor/gitignore/Python.gitignore
+++ b/vendor/gitignore/Python.gitignore
@@ -66,7 +66,7 @@ docs/_build/
 # PyBuilder
 target/
 
-# IPython Notebook
+# Jupyter Notebook
 .ipynb_checkpoints
 
 # pyenv
diff --git a/vendor/gitignore/Rust.gitignore b/vendor/gitignore/Rust.gitignore
index cb14a420640b9af2da76a8a73650fba0929bfeb8..50281a44270e0fe457f02c7c9edebe234eaa708d 100644
--- a/vendor/gitignore/Rust.gitignore
+++ b/vendor/gitignore/Rust.gitignore
@@ -5,3 +5,6 @@
 # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
 # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
 Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
index f620fad23ebe1bf6f2a9f565963d1a3032241bba..1afbaf197f4c08bf0969488219ce50cb9084bcec 100644
--- a/vendor/gitignore/TeX.gitignore
+++ b/vendor/gitignore/TeX.gitignore
@@ -61,6 +61,15 @@ acs-*.bib
 # fixme
 *.lox
 
+# feynmf/feynmp
+*.mf
+*.mp
+*.t[1-9]
+*.t[1-9][0-9]
+*.tfm
+*.[1-9]
+*.[1-9][0-9]
+
 #(r)(e)ledmac/(r)(e)ledpar
 *.end
 *.?end
diff --git a/vendor/gitignore/UnrealEngine.gitignore b/vendor/gitignore/UnrealEngine.gitignore
index be0e4913c3a7194e9aa462dcaf9d0774c5c3216d..beec7b91f15c2f18a5efa6931605f27424f9ee64 100644
--- a/vendor/gitignore/UnrealEngine.gitignore
+++ b/vendor/gitignore/UnrealEngine.gitignore
@@ -1,6 +1,9 @@
 # Visual Studio 2015 user specific files
 .vs/
 
+# Visual Studio 2015 database file
+*.VC.db
+
 # Compiled Object files
 *.slo
 *.lo
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index 1b86e7ec9181af321f345052de570448b20552fb..09e407344ca83384d7122ec296e7b2abe42176f4 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -1,11 +1,14 @@
 ## Ignore Visual Studio temporary files, build results, and
 ## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
 
 # User-specific files
 *.suo
 *.user
 *.userosscache
 *.sln.docstates
+*.vcxproj.filters
 
 # User-specific files (MonoDevelop/Xamarin Studio)
 *.userprefs
@@ -44,6 +47,7 @@ dlldata.c
 project.lock.json
 project.fragment.lock.json
 artifacts/
+Properties/launchSettings.json
 
 *_i.c
 *_p.c
@@ -238,6 +242,9 @@ FakesAssemblies/
 # Visual Studio 6 workspace options file
 *.opt
 
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
 # Visual Studio LightSwitch build output
 **/*.HTMLClient/GeneratedArtifacts
 **/*.DesktopClient/GeneratedArtifacts
diff --git a/vendor/gitlab-ci-yml/Clojure.gitlab-ci.yml b/vendor/gitlab-ci-yml/Clojure.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f066285b1adf0b2ab48f4789ad2a027a97d35fcc
--- /dev/null
+++ b/vendor/gitlab-ci-yml/Clojure.gitlab-ci.yml
@@ -0,0 +1,22 @@
+# Based on openjdk:8, already includes lein
+image: clojure:lein-2.7.0
+# If you need to configure a database, add a `services` section here
+# See https://docs.gitlab.com/ce/ci/services/postgres.html
+# Make sure you configure the connection as well
+
+before_script:
+  # If you need to install any external applications, like a 
+  # postgres client, you may want to uncomment the line below:
+  # 
+  #- apt-get update -y
+  #
+  # Retrieve project dependencies
+  # Do this on before_script since it'll be shared between both test and
+  # any production sections a user adds
+  - lein deps
+
+test:
+  script:
+  # If you need to run any migrations or configure the database, this 
+  # would be the point to do it.  
+  - lein test
diff --git a/vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml b/vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e8da49a935e3e90c4641004edeb02cfc38c9eb64
--- /dev/null
+++ b/vendor/gitlab-ci-yml/Crystal.gitlab-ci.yml
@@ -0,0 +1,37 @@
+# This file is a template, and might need editing before it works on your project.
+# Official language image. Look for the different tagged releases at:
+# https://hub.docker.com/r/crystallang/crystal/
+image: "crystallang/crystal:latest"
+
+# Pick zero or more services to be used on all builds.
+# Only needed when using a docker container to run your tests in.
+# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-service
+# services:
+#   - mysql:latest
+#   - redis:latest
+#   - postgres:latest
+
+# variables:
+#   POSTGRES_DB: database_name
+
+# Cache shards in between builds
+cache:
+  paths:
+    - libs
+
+# This is a basic example for a shard or script which doesn't use
+# services such as redis or postgres
+before_script:
+  - apt-get update -qq && apt-get install -y -qq libxml2-dev
+  - crystal -v # Print out Crystal version for debugging
+  - shards
+
+# If you are using built-in Crystal Spec.
+spec:
+  script:
+  - crystal spec
+
+# If you are using minitest.cr
+minitest:
+  script:
+  - crystal test/spec_test.cr # change to the file(s) you execute for tests